AsyncTaskLoader doesn't run
Asked Answered
B

7

30

I want to implement an AsyncTaskLoader in my project using the Compatibility Package, so I followed the Loader manual in Android Docs.

The problem is that the Loader does nothing, it seems loadInBackground() is never called

Any idea of what's wrong in my code? (ExpandableListFragment extends Fragment,but doesn't override any critical method )

Thank you :-)

/**EDIT:

I realized (late, I'm a moron) that AsyncTaskLoader is an abstract class so I need to subclass it... m(__)m I leave the question in case someone comes here behind me, who knows...

public class AgendaListFragment extends ExpandableListFragment implements
        LoaderManager.LoaderCallbacks<JSONArray> {

    private TreeMap<Integer, ArrayList<Evento>> mItems = new TreeMap<Integer, ArrayList<Evento>>();
    private AgendaListAdapter mAdapter;
    private ProgressBar mProgressBar;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_agenda, container);
        mProgressBar = (ProgressBar) root.findViewById(R.id.loading);
        return root;

    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mAdapter = new AgendaListAdapter(getActivity());
        setListAdapter(mAdapter);

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

    }

    @Override
    public Loader<JSONArray> onCreateLoader(int arg0, Bundle arg1) {
        mProgressBar.setVisibility(View.VISIBLE);
        return new AsyncTaskLoader<JSONArray>(getActivity()) {
            @Override
            public JSONArray loadInBackground() {

                return getDataFromService(AgendaServices.LISTADO_MES);

            }

        };
    }

    @Override
    public void onLoadFinished(Loader<JSONArray> loader, JSONArray data) {

        // Some stuff to turn JSONArray into TreeMap

        mProgressBar.setVisibility(View.GONE);
        mAdapter.setItems(mItems);

    }

    @Override
    public void onLoaderReset(Loader<JSONArray> arg0) {
        mAdapter.setItems(null);
        mProgressBar.setVisibility(View.VISIBLE);

    }

}
Birthmark answered 22/12, 2011 at 15:31 Comment(2)
Your AsyncTaskLoader seems to be missing lots of stuff, like deliverResults(). I have two AsyncTaskLoader implementations in my LoaderEx project you might want to examine for comparison purposes: github.com/commonsguy/cwac-loaderexSkelp
It seems I'm a mess and I didn't realize it's an abstract class... So now I know why I only found a few examples subclassing it. Thank you!Interpretative
S
63

I think the best solution for the Compatibility package is to override the AsyncTaskLoader.onStartLoading method.

e.g.

@Override
protected void onStartLoading() {
  if(dataIsReady) {
    deliverResult(data);
  } else {
    forceLoad();
  }
}
Shine answered 12/1, 2012 at 5:55 Comment(3)
I did this at the end :-D. Thanks!Interpretative
Yeah thanks david! Wrote this up into an post with source code: blog.blundell-apps.com/…Villose
Checking for takeContentChanged seems an important step too.Jamikajamil
C
8

This is exactly a fix but it should work. I am pretty sure the compatibility library is broken. Try this:

getLoaderManager().initLoader(0, null, this).forceLoad();
Ceria answered 27/12, 2011 at 0:40 Comment(2)
This is another way to do it :-D.Interpretative
This would work, but it's probably better practice to implement your Loader<D> properly (as davidshen84 suggests, by implemenitng the onStartLoading() method) instead of calling forceLoad() directly in your Activity and/or Fragment.Elflock
S
3

Cheok Yan Cheng is absolutely right:

Checking for takeContentChanged seems an important step too.

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();
        }
    }
}
Starrstarred answered 20/10, 2013 at 18:10 Comment(0)
J
1

Looking at discussion at https://code.google.com/p/android/issues/detail?id=14944, checking for takeContentChanged seems to be important step too.

protected void onStartLoading() {
    if (mCursor != null) {
        deliverResult(mCursor);
    }
    if (takeContentChanged() || mCursor == null) {
        forceLoad();
    }
}
Jamikajamil answered 19/3, 2013 at 7:26 Comment(3)
There is no point in using takeContentChanged here - it won't affect result. If mCursor is null - forceLoad will be called. If it is not - deliverResult will be called.Erastian
Why do you say takeContentChanged has no point? developer.android.com/reference/android/content/… Google's example does make use of that.Jamikajamil
Because when an activity enter onPause() and comes back with onResume() the loader is notified and will call onStartLoading() again. So takeContentChanged() (if onContentChanged() was called previously) will force to update mCursor with last available changes. However, on the first if I'd put instead if (mCursor != null && !takeContentChanged()).Biebel
A
0

I took the source code of CursorLoader from android framework, and wrote a CustomTaskLoader<T> class to ease the job.

https://github.com/Palatis/danbooru-gallery-android/blob/new_api/DanbooruGallery/src/main/java/tw/idv/palatis/danboorugallery/android/content/CustomTaskLoader.java

you basically implement these two functions:

public abstract T runTaskInBackground(CancellationSignal signal);
public abstract void cleanUp(T oldResult);

see the usage in the activities and fragments, for example this one: (well my code just ignores the CancellationSignal, it's a TODO in my list, but you're free to use it.)

https://github.com/Palatis/danbooru-gallery-android/blob/new_api/DanbooruGallery/src/main/java/tw/idv/palatis/danboorugallery/PostListFragment.java

return new CustomTaskLoader<Cursor>(getActivity().getApplicationContext()) 
{
    @Override
    public Cursor runTaskInBackground(CancellationSignal signal)
    {
        return SiteSession.getAllPostsCursor(PostListAdapter.POST_COLUMNS);
    }

    @Override
    public void cleanUp(Cursor oldCursor)
    {
        if (!oldCursor.isClosed())
            oldCursor.close();
    }
}
Archiepiscopate answered 31/1, 2014 at 8:41 Comment(0)
B
0

I have had the same problem after migrating from CursorLoader to AsyncTaskLoader.

documentation says: Subclasses of Loader<D> generally must implement at least onStartLoading(), onStopLoading(), onForceLoad(), and onReset().

AsyncTaskLoader extends Loader but not implements onStartLoading(), onStopLoading(), onReset(). You must implement it by yourself!

@davidshen84 proposed good solution. I only added checking for takeContentChanged.

@Override
protected void onStartLoading() {
    try {
        if (data != null) {
            deliverResult(data);
        }
        if (takeContentChanged() || data == null) {
            forceLoad();
        }

        Log.d(TAG, "onStartLoading() ");
    } catch (Exception e) {
        Log.d(TAG, e.getMessage());
    }
}

Using forceLoad() is ok (not a bad practice). See what documentation says:
You generally should only call this when the loader is started - that is, isStarted() returns true.

Beckybecloud answered 17/8, 2016 at 10:31 Comment(0)
T
-2

I was still having the problem that loading of data was not called. I finally removed the AsyncTaskLoader (the support library version) and used only AsyncTask (not from support library) to do the job. And it worked.

It could be enough for your needs too.

Description and example: http://developer.android.com/reference/android/os/AsyncTask.html.

You have to extend the class AsyncTask.

The method doInBackground will do the work and in the method onPostExecute you will get the result. For starting the AsyncTask, you will call the method execute on its instance. See the link.

Tyner answered 1/7, 2013 at 6:10 Comment(1)
AsyncTask and AsyncTaskLoader are quite different things...the AsyncTaskLoader has several advantages. You should research a little bit moreDupre

© 2022 - 2024 — McMap. All rights reserved.