Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Effective AsyncTaskLoader

Effective AsyncTaskLoader

Basics, Pitfalls and Advanced Usage of AsyncTaskLoader in Android.

Hiroshi Kurokawa

March 27, 2015
Tweet

More Decks by Hiroshi Kurokawa

Other Decks in Technology

Transcript

  1. What is AsyncTaskLoader? Loader and LoaderManager provides a framework to

    execute an asynchronous task perceiving Activity/Fragment lifecycle. AsyncTaskLoader is an Loader embedding an AsyncTask in it. @Override public void onCreate(Bundle savedInstanceState) { ... getLoaderManager().initLoader(LOADER_ID, null, this); } @Override public Loader<List<Data>> onCreateLoader(int id, Bundle args) { return new DataLoader(); } @Override public void onLoadFinished(Loader<List<Data>> loader, List<Data> data) { mAdapter.clear(); mAdapter.addAll(data) } @Override
  2. AsyncTaskLoader Basics Pros Lifecycle Management Destroyed when the parent Activity/Fragment

    is destroyed. Context Leak Protection Running on the Application Context not on an Activity Context. Data Caching No need to re-run the asynchronous task after configuration change. Throttable Can set Throttle so that the requests are not issued too frequently. Cons Complicated!
  3. How It Works? LoaderManager runs on Application Context. It re-attaches

    to the Activity/Fragment when its configuration is changed and re- created. It is especially useful when the screen is rotated while data is being fetched. from Efficient Android Threading
  4. Custom AsyncTaskLoader Sample: http://developer.android.com/reference/android/content/AsyncTaskL Basically, you just need to override

    only loadInBackground(). Very Easy! @Override public Data loadInBackground() { Data data = doAnyHeavyTask(); return data; } However...
  5. Pitfall 1 Have to initialize a loader within Fragment#onActivityCreated() not

    in Fragment#onCreate(). @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // In support library r8, calling initLoader for a fragment in a FragmentPagerAdapter // in the fragment's onCreate may cause the same LoaderManager to be dealt to multiple // fragments because their mIndex is -1 (haven't been added to the activity yet). Thus, // we do this in onActivityCreated. this.setListShown(false); this.getLoaderManager().initLoader(LOADER_ID_CATEGORIES, this.getArguments(), this); ...
  6. Pitfall 2 Loader#loadInBackground() is not run on UI thread. Should

    use deliverResult() instead. public abstract class CustomAsyncTaskLoader extends AsyncTaskLoader<Data> { @Override public Data loadInBackground() { if (this.mListener != null) { // Notify the loading status is changed. this.mListener.onLoadStarted(this); } ... } } public class SampleActivity extends Activity { @Override public void onLoadStarted(final CustomAsyncTaskLoader<Data> loader) { if (!this.isFinishing() && loader instanceof CustomAsyncTaskLoader) { this.runOnUiThread(new Runnable() { @Override public void run() { SampleActivity.this.onLoadingStatusChanged(LoadingStatus.LOADING); } }); } }
  7. Pitfall 3 After AsyncTaskLoader#loadInBackground() is executed, deliverResult() might or might

    not be executed. It is not called when reset() is executed just after loadInBackground(). public abstract class CustomAsyncTaskLoader extends AsyncTaskLoader<Data> { private int mNextPage; @Override public Data loadInBackground() { // Fetch data on the next page ... return data; } // If the AsyncTaskLoader is canceled after loadInBackground(), this method is not called. @Override public void deliverResult(Data data) { if (data != null) { // Should increment the page number here. Not do that in loadInBackground()! this.mNextPage++;
  8. Advanced: Data Cached AsyncTaskLoader This custom AsyncTaskLoader caches the previous

    data and deliver it to the Activity/Fragment if it is not delivered yet when it starts. Code is at https://gist.github.com/hkurokawa/c61b3d7805aa74d9d111#file- cachedasynctaskloader-java public abstract class CachedAsyncTaskLoader<T> extends AsyncTaskLoader<T> { private T mCached; @Override protected void onStartLoading() { // Return the cached data if exists if (this.mCached != null) { deliverResult(this.mCached); return; } // If data source is changed or cached data is null, try to get data if (this.mCached == null) { this.forceLoad(); }
  9. Advanced: Paging AsyncTaskLoader This custom AsyncTaskLoader can do paging. It

    appends the retrieved data in the next page to the current list and delivers the entire list. Code is at https://gist.github.com/hkurokawa/c61b3d7805aa74d9d111#file- paingasynctaskloader-java public class PagesLoader extends AsyncTaskLoader<Page> { private static final int NUM_ARTICLES_PER_PAGE = 20; private boolean mHasMoreResults; private Page mPage; public PagesLoader(Context context) { super(context); this.mPage = new Page(null, null); this.mHasMoreResults = true; } @Override public Page loadInBackground() { if (this.hasMoreResults()) { // Use mPage.getNextToken() to retrieve the next page. Page response = retrieveNextPage(this.mPage.getNextToken(), NUM_ARTICLES_PER_PAGE); List<Article> articles = response.getArticles();
  10. Conclusion AsyncTaskLoader has some features AsyncTask does not have and

    is valuable in some situations. AsyncTaskLoader is very rather complicated and not easy to take advantage of. RxJava/RxAndroid might be better choice to achieve asynchronous task these days.