Android AsyncTaskLoader doesn't start loadInBackground?

JavaAndroid

Java Problem Overview


I am trying to implement a loader example on Android but can't get it to start the loader. I am using the following code. It will hit the "Create Loader" but it will never reach the "Loading started" log message. Am I missing a call that I need?

Activity:

    public class TestingZoneActivity extends ListActivity implements LoaderCallbacks<ArrayList<Content>>{

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
    
            getLoaderManager().initLoader(0, null, this);
        }

        @Override
    	public Loader<ArrayList<Content>> onCreateLoader(int id, Bundle args) {
        	Log.e("TEST", "Create Loader");
	        return new ImageLoader(this);
    	}

        @Override
    	public void onLoadFinished(Loader<ArrayList<Content>> loader, ArrayList<Content> data) {
        	setListAdapter(new ImageAdapter(this, data));
    	}

        @Override
    	public void onLoaderReset(Loader<ArrayList<Content>> loader) {
        	setListAdapter(null);
    	}
    }

Loader:

    public class ImageLoader extends AsyncTaskLoader<ArrayList<Content>> {

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

        @Override
    	public ArrayList<Content> loadInBackground() {
        	Log.e("TEST", "Loading started");
    	}

    }

Java Solutions


Solution 1 - Java

I had the same problem using the compatibility library. I solved it by calling forceLoad

getLoaderManager().initLoader(0, null, this).forceLoad();

Obviously the documentation on AsyncLoader is lacking and this problem also exists on HoneyComb. More information can be found here

The official example of AsyncTaskLoader is also calling forceLoad() so its not a bug, but i still think that that behavior is not very intuitive.

Solution 2 - Java

Overriding loadInBackground() is not enough.

Have a look at the AppListLoader on http://developer.android.com/reference/android/content/AsyncTaskLoader.html .

At least add these two methods to your loader:

		@Override
		protected void onStartLoading() {
			if (takeContentChanged())
				forceLoad();
		}
		
		@Override
		protected void onStopLoading() {
			cancelLoad();
		}

and call onContentChanged() from its constructor.

Solution 3 - Java

rrayst's advice is quite compact. If you write your method like this:

protected void onStartLoading() {
    forceLoad();
}

you ''ll notice that when a child activity comes up and then you return to the parent one, onStartLoading (and so loadInBackground) are called again!

What can you do? Set an internal variable (mContentChanged) to true inside the constructor; then check this variable inside onStartLoading. Only when it's true, start loading for real:

package example.util;

import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;

public abstract class ATLoader<D> extends AsyncTaskLoader<D> {
	
	public ATLoader(Context context) {
		super(context);
		// run only once
		onContentChanged();
	}
	
	@Override
	protected void onStartLoading() {
		// That's how we start every AsyncTaskLoader...
		// -  code snippet from  android.content.CursorLoader  (method  onStartLoading)
		if (takeContentChanged()) {
			forceLoad();
		}
	}
	
	@Override
	protected void onStopLoading() {
    	cancelLoad();
	}
}

Solution 4 - Java

Since none of the answers here (besides the accepted one) alone helped me to solve this, here is how it worked for me.

I don't think the accepted answer is the correct solution, since it causes loadInBackground() to be called more often than necessary, i.e. on orientation change, which does not happen when properly overriding the following methods in the loader as well:

@Override
public void deliverResult(final List<Participant> data) {
	participants = data;
	
    if (isStarted()) {
        super.deliverResult(data);
    }

}

@Override
protected void onStartLoading() {
	if (takeContentChanged() || participants == null) {
		forceLoad();
	}
}

Solution 5 - Java

If you are using a custom loader, you can save the last data reference, and have it available via a getter. when the user rotates his screen, you can get the loader back from getLoaderManager().getLoader method, and then return back the reference. For my testing I noticed that startLoadering goes all the way to CustomLoader.onLoadFinished but the result is never deliver to activity.onLoadFinished.I suspect the activity reference gets lost upon rotation. By the way the great thing about creating loaders is they are persistent through LoaderManager. Think of it as another flavor of headless fragments.. lol.

Loader loader  =  getLoaderManager().getLoader(LOADER_ID);

if( loader == null )
{
    getLoaderManager().initLoader(LOADER_ID, null, MyActivity.this );
}
else
{
    listAdapter.addAll(((CustomLoader) loader).getLastData());
}

Solution 6 - Java

I've found that each of the above solutions have issues, especially when the app starts while the screen is turned off, and the loading takes a few moments.

Here's my solution (base on this):

https://stackoverflow.com/a/22675607/878126

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBobbake4View Question on Stackoverflow
Solution 1 - JavaRenardView Answer on Stackoverflow
Solution 2 - JavarraystView Answer on Stackoverflow
Solution 3 - JavaKitKatView Answer on Stackoverflow
Solution 4 - JavaTorsten RömerView Answer on Stackoverflow
Solution 5 - JavaJuan MendezView Answer on Stackoverflow
Solution 6 - Javaandroid developerView Answer on Stackoverflow