0

My app downloads JSON data from Internet and displays it as a RecyclerView list. This is implemented through LoaderManager/Loader means. Action is initiated by clicking a button.

In onCreate I call initLoader() if that specific loader already exists, in order to give it new callbacks, bound to the new Activity (I do this to ensure user doesn't lose progress if he rotates the screen mid-download). Now I have arranged for organized list data to be retained upon screen rotation. For example, if I load data and then rotate the screen, that call to initLoader() will correctly deduce the data had already been loaded and will call onLoadFinished() (I suspect loaded data is cached somehow internally).

What baffles me is that loadInBackground() will somehow be called also, absolutely needlessly.

Reading documentation didn't quite clarify things for me.

How do I merely pass new callbacks to my loader upon onCreate() without needless operations being called?

Edit: Here is AsyncTaskLoader code.

@NonNull
@Override
public Loader<String> onCreateLoader(int id, final Bundle args) {
    return new AsyncTaskLoader<String>(this) {
        @Override
        protected void onStartLoading() {
            Log.w("DBG", "onStartLoading");
            if (args == null) return;
            Log.w("DBG", "about to forceLoad");
            forceLoad();
        }

        @Override
        public String loadInBackground() {
            Log.w("DBG", "loadInBackground");

            String baseId = args.getString("baseId");
            String quoteId = args.getString("quoteId");
            String exchange = args.getString("exchange");

            Uri uri = buildUri(baseId, quoteId, exchange);

            String txt = uri.toString();
            Log.i("Uri built: ", txt);

            String response = "";
            try {
                URL url = new URL(uri.toString());
                response = getResponseFromHttpUrl(url);
            } catch (IOException e) {
                e.printStackTrace();
            }

            final int PAUSE_SEC = 5;

            try {
                Thread.sleep(PAUSE_SEC * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return response;
        }
    };
}
Drinkwater
  • 151
  • 6

1 Answers1

0

As per the Making loading data lifecycle aware blog post:

we can now hold onto results in a member variable and immediately return them back after configuration changes by immediately calling deliverResult() in our onStartLoading() method. Note how we don’t call forceLoad() if we have cached data — this is how we save ourselves from constantly reloading the data!

Checking if your args is null is not enough, since your second call to initLoader() generally passes the same args to your Loader. Instead, it is up to your AsyncTaskLoader to cache the results and use the existence of already loaded data to specifically not call forceLoad() from onStartLoading().

Note that as per that same blog post:

If you’re looking for a modern, flexible solution to this problem that doesn’t rely on Loaders (the chosen solution here), check out the Lifecycle Aware Data Loading with Architecture Components blog post.

As you might find a solution based on a ViewModel (which also survives configuration changes) and LiveData (for observing the results in your Activity/Fragment) much less code to write and be easier to understand.

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443