2

I'm having a bit of an issue with calling restartLoader() from the outside of a ListFragment. The sequence goes according to Tap an "Add"-button which opens an Activity -> Enter data -> Tap Save, which starts an AsyncTask to store data on a server. Upon a successful data transfer, return to the previous Activity -> Have the Activity call a method in the ListFragment that runs getLoaderManager().restartLoader().

This, without fail, always creates a "java.lang.IllegalStateException: Fragment ListsFragment{42481238} not attached to Activity"-error. If I move the restartLoader() to onResume it works slightly better, but I have other buttons that directly modifies the data connected to the ListFragments, which still trigger an IllegalStateException no matter what.

Excerpt error message from logcat

E/AndroidRuntime(29594): java.lang.IllegalStateException: Fragment ListsFragment{42450b18} not attached to Activity
E/AndroidRuntime(29594):        at android.support.v4.app.Fragment.getLoaderManager(Fragment.java:768)
E/AndroidRuntime(29594):        at testpager.ListsFragment.resetList(ListsFragment.java:122)

What am I missing? How can I refresh a ListFragment from a FragmentActivity without triggering this exception?

MBMJ
  • 5,323
  • 8
  • 32
  • 51
shellström
  • 1,417
  • 13
  • 29

2 Answers2

3

You shouldn't refresh your ListFragment from the FragmentActivity... this goes against both the fundamental design guidelines of both Fragments (which are supposed to be designed for reuse and not tied to any one specific activity) and the LoaderManager (which is supposed belongs to a single Activity or Fragment). You should have your ListFragment implement the LoaderManager.LoaderCallbacks<D> interface instead.

On a separate note, Loaders are supposed to receive notifications when their underlying data source is changed, signalling them to perform a new asynchronous load and return the results back to the callback's onLoadFinished method. If you must rely on restartLoader to get the most up-to-date data, then you've done something wrong (i.e. your ContentObserver is not being notified of the change that would have signaled the Loader to requery its data).

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
  • Sorry for my inexcusably late response. My project was put on ice for quite a while but I'm resurfacing it. – shellström Dec 03 '12 at 19:59
  • Sorry for my inexcusably late response. I am actually using LoaderManager.LoaderCallbacks and Christians Loader http://stackoverflow.com/questions/7182485/usage-cursorloader-without-contentprovider I tried making an implementation that uses a Loader but not necessarily including an entire content provider since I'll only be running this in my own app. I've read this http://stackoverflow.com/a/4245672/975641 and it seems a provider might be overkill for this. Any thoughts? Might be that this is an entire new question. Just trying to clarify. – shellström Dec 03 '12 at 20:12
  • 3
    `Commonsware` always talks trash about `ContentProvider`s :P. The argument is that CPs are overkill for applications that don't share their data... I almost agree with this, *BUT* the one thing I really like about the CP + loader combination is that it comes with a global notification system. That is, if you call `setNotificationUri()` on your cursor in the CP query method, and `notifyUri()` in your insert/delete methods, the cursors will be notified of content changes throughout your *entire* application (i.e. no polling required!). There isn't an easy way to do this w/o the ContentResolver. – Alex Lockwood Dec 06 '12 at 16:07
  • 1
    There are also a bunch of libraries out there that can automate most of the process of writing a CP for you... so honestly, I don't think writing a CP is really that big of a deal. – Alex Lockwood Dec 06 '12 at 16:09
0

If I understand correctly, you have Activity A which created a loader that has a menu option to save data through Activity B. Once Activity B has saved the data, you want the loader in Activity A to be restarted... am I right?

If so, try doing this in Activity B:

public interface onSavedDataListener { 
     void DoneSavingData(); //add parameters if needed
}
... //Whatever other code you have
 //This method should be called from your AsyncTask onFinished() method
 public void dataSaved() { //add parameters if needed
    onSavedDataListener activity = (onSavedDataListener) getActivity();
    activity.DoneSavingData(); //make sure you add the same parameters as you did above
}

Then in Activity A, you would implement the listener

public class ActivityA extends ListFragment implements onSavedDataListener {

    ... //your other code here

    //Add the listener function
    public void DoneSavingData() { //make sure any parameters you have get added here
        getLoaderManager().restartLoader(id, args, loadercallback);
    }

    ...

In essence, you create a listener object in Activity B that is implemented in Activity A. I hope I'm right and that it will help you out...

wileyCoyote
  • 829
  • 5
  • 8
  • BTW, if you are using the support pack for backward compatibility to previous SDKs, you should be calling getSupportLoaderManager() instead... – wileyCoyote Jul 25 '12 at 02:06