What is the scope of a LoaderManager?
Asked Answered
S

2

16

When identifying Loaders in your LoaderManager, you use unique ids. I'm asking about how unique those ids have to be.

Does every activity and fragment have its own LoaderManager? Do fragments use the LoaderManager of the Activity they're attached to? Is there only one LoaderManager that the application owns?

Bonus points if you can tell me how it's possible to change which LoaderManager you're using. If I want every fragment in my Activity to use the same LoaderManager (some of them are pulling the same data and sharing Loaders would be nice), is that possible?

Screw answered 6/9, 2011 at 20:20 Comment(1)
Understanding the LoaderManager (part 2)Crump
E
8

I'm currently porting my application to the android compatibility package (mainly for CursorLoader and Fragments). I'm currently trying to share a CursorLoader between two fragments to spare a query to my ContentProvider. Welcome to my world! ;)

A simple use case:

− DummyActivity extends FragmentActivity / Log.d(Constants.LOGTAG, "DummyActivity.onCreate " + getSupportLoaderManager().toString());

− DataFragment extends Fragment implements LoaderManager.LoaderCallbacks / Log.d(Constants.LOGTAG, "DataFragment.onCreate " + getLoaderManager().toString());

− ReportFragment extends Fragment implements LoaderManager.LoaderCallbacks / Log.d(Constants.LOGTAG, "ReportFragment.onCreate " + getLoaderManager().toString());

DummyActivity instanciates the DataFragment and the later instanciates the ReportFragment. The logcat output shows differents addresses for each LoaderManager. As a first conclusion, each Fragment seems to have a proper LoaderManager…

I will continue and update if I can answer to your (our ;) ) question. If you have made any progress, please share your valuable knowledge.

Update:

My assumption is that the loader ids are only associated with a local scope of a LoaderManager for a specific fragment to enable several local loaders to be associated with the fragment (so you can return a different loader in onCreateLoader based on the id int arg and the initLoader calls).

So far I managed to "reuse" a Loader (… or not):

− First, I have enabled LoaderManager debugging with getSupportLoaderManager().enableDebugLogging(true); in the DummyActivity onCreate method.

− Then I have called getActivity().getSupportLoaderManager().initLoader(78, null, this); from the onCreate methods of both DataFragment and ReportFragment.

− DataFragment exposes the CursorLoader created by the onCreateLoader method via a setter on a mCursorLoader private member.

− The ReportFragment onCreateLoader returns the DataFragment CursorLoader (after retrieving the Fragment with findFragmentByTag).

The filtered (and slightly obfuscated) logcat output:

      DummyApp  D  DummyActivity.onCreate
      DummyApp  D  DataFragment.newInstance
      DummyApp  D  ReportFragment.newInstance
      DummyApp  D  DataFragment.onCreate
 LoaderManager  V  initLoader in LoaderManager{405a19d0 in SpecificAction{4059ee98}}: args=null
      DummyApp  D  DataFragment.onCreateLoader
 LoaderManager  V    Created new loader LoaderInfo{405a2298 #78 : CursorLoader{405a22e0}}
      DummyApp  D  DataFragment.onCreate
      DummyApp  D  DataFragment.onActivityCreated
      DummyApp  D  ReportFragment.onCreate
 LoaderManager  V  initLoader in LoaderManager{405a19d0 in DummyActivity{4059ee98}}: args=null
 LoaderManager  V    Re-using existing loader LoaderInfo{405a2298 #78 : CursorLoader{405a22e0}}
      DummyApp  D  SpecificActionReportFragment.onCreate
      DummyApp  D  SpecificActionReportFragment.onActivityCreated
 LoaderManager  V  Starting in LoaderManager{405a19d0 in DummyActivity{4059ee98}}
 LoaderManager  V    Starting: LoaderInfo{405a2298 #78 : CursorLoader{405a22e0}}
 DummyProvider  D  query called
 DummyProvider  D  […]       
 DummyProvider  D  [end of query]
 LoaderManager  V  onLoadComplete: LoaderInfo{405a2298 #78 : CursorLoader{405a22e0}}
 LoaderManager  V    onLoadFinished in CursorLoader{405a22e0 id=78}: CursorWrapperInner{405afb20}
      DummyApp  D  ReportFragment.onLoadFinished
      DummyApp  D  ReportFragment.displayActionReport
      DummyApp  D  DummyActivity.setReportViewsVisibility
      DummyApp  D  ReportFragment.setSaveReportImageViewVisibility

The two fragments are added from the DummyActivity onCreate method (different from the described use case, but that changes nothing to the issue we are working on). Unfortunately, the loader is reassigned to the latest fragment calling it (here ReportFragment)… and DataFragment.onLoadFinished is never called. As a consequence, the ReportFragment looks good but the DataFragment is not up-to-date since the update is called from the onLoadFinished of this class.

I assume that there is an underlying unregister call then a register call on the CursorLoader.

To be continued…

Elias answered 29/9, 2011 at 23:13 Comment(4)
Thanks for the answer! I've been doing some iOS stuff, but should have time to play around with and finish answering this question in a week or so. Will report back! :)Screw
I will work on it too the next days because my workmates are waiting for.Elias
I will eventually optimize my ContentProvider. Sorry to not deserve the bonus point… ;)Elias
Did you manage to find out how to use one LoaderManager in multiple fragments/activities? It would be of great value for me.Mlawsky
G
0

Yes. It worked for me. I have 3 different Fragments in a Navigation Drawer where the same data is populated in different ListViews. (All Fragments are a part of the SAME Activity).

My AsyncTaskLoader:

public class MyTaskLoader extends AsyncTaskLoader<HashMap<String, Integer>> {

public MyTaskLoader(Context context) {
    super(context);
}

@Override
public HashMap<String, Integer> loadInBackground() {
...
return hashMap;
}

...
}

Use the Same Loader Id in all Fragments.

Fragment1:

public class Fragment1 extends BaseFragment implements LoaderManager.LoaderCallbacks<HashMap<String, Integer>> {
@Override
public void onCreate(Bundle savedInstanceState) {

//initialize adapter

getActivity().getSupportLoaderManager().initLoader(0, null, this);

}

@Override
public Loader<HashMap<String, Integer>> onCreateLoader(int arg0, Bundle arg1) {
    // TODO Auto-generated method stub

    return new MyTaskLoader(getActivity());
}

@Override
public void onLoadFinished(Loader<HashMap<String, Integer>> arg0,
        HashMap<String, Integer> data) {
    // TODO Auto-generated method stub

    listAdapter.setData(data.keySet());

}

@Override
public void onLoaderReset(Loader<HashMap<String, Integer>> arg0) {
    // TODO Auto-generated method stub

    listAdapter.setData(null);
}
}

Use the same Id for Fragment2:

public class Fragment2 extends BaseFragment implements LoaderManager.LoaderCallbacks<HashMap<String, Integer>> {
@Override
public void onCreate(Bundle savedInstanceState) {

//initialize adapter

getActivity().getSupportLoaderManager().initLoader(0, null, this);

}

@Override
public Loader<HashMap<String, Integer>> onCreateLoader(int arg0, Bundle arg1) {
    // TODO Auto-generated method stub

    return new MyTaskLoader(getActivity());
}

@Override
public void onLoadFinished(Loader<HashMap<String, Integer>> arg0,
        HashMap<String, Integer> data) {
    // TODO Auto-generated method stub

    listAdapter.setData(data.keySet());

}

@Override
public void onLoaderReset(Loader<HashMap<String, Integer>> arg0) {
    // TODO Auto-generated method stub

    listAdapter.setData(null);
}
}

The adapter should be initialized before initializing the loader. Works so far. But, is this the right way? Is there a better method for using a common loader for multiple Fragments?

Gerdes answered 6/1, 2015 at 5:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.