-
-
Save natuanorg/78734ea9b3e8ba62588e7e386f0a6121 to your computer and use it in GitHub Desktop.
AsyncTaskLoader Sample
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* <p> | |
* This loader caches the result so that it can be taken later - for example, after configuration | |
* was changed and activity is re-created. | |
* </p> | |
* Created by hiroshi on 2014/12/03. | |
*/ | |
public abstract class CachedAsyncTaskLoader<T> extends AsyncTaskLoader<T> { | |
private T mCached; | |
private Throwable mError; | |
private LoaderListener<T> mListener; | |
public CachedAsyncTaskLoader(Context context) { | |
super(context); | |
this.init(); | |
} | |
private void init() { | |
this.mError = null; | |
} | |
public void setListener(LoaderListener<T> listener) { | |
this.mListener = listener; | |
} | |
/** | |
* Called when there is new data to deliver to the client. The | |
* super class will take care of delivering it; the implementation | |
* here just adds a little more logic. | |
*/ | |
@Override | |
public void deliverResult(T data) { | |
// If the loader is reset and it is going to finish, purge the cached data | |
if (this.isReset()) { | |
if (this.mCached != null) { | |
this.mCached = null; | |
} | |
return; | |
} | |
this.mCached = data; | |
if (this.isStarted()) { | |
super.deliverResult(data); | |
} | |
} | |
/** | |
* Handles a request to start the Loader. | |
*/ | |
@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.takeContentChanged() || this.mCached == null) { | |
this.forceLoad(); | |
} | |
} | |
/** | |
* Handles a request to stop the Loader. | |
*/ | |
@Override | |
protected void onStopLoading() { | |
this.cancelLoad(); | |
} | |
/** | |
* Handles a request to completely reset the Loader. | |
*/ | |
@Override | |
protected void onReset() { | |
super.onReset(); | |
// Ensure the loader is stopped | |
this.onStopLoading(); | |
// Initialize status and delete the cached data so that, in the next time, new data is fetched | |
this.init(); | |
this.mCached = null; | |
} | |
@Override | |
public T loadInBackground() { | |
if (this.mListener != null) { | |
this.mListener.onLoadStarted(this); | |
} | |
T data = null; | |
try { | |
data = this.load(); | |
} catch (Exception e) { | |
this.mError = e; | |
} | |
if (this.mListener != null) { | |
this.mListener.onLoadFinished(this, data); | |
} | |
return data; | |
} | |
abstract protected T load(); | |
public void refresh() { | |
this.reset(); | |
this.startLoading(); | |
} | |
public Throwable getError() { | |
return this.mError; | |
} | |
public boolean hasError() { | |
return this.mError != null; | |
} | |
public static interface LoaderListener<T> { | |
public void onLoadStarted(final CachedAsyncTaskLoader<T> loader); | |
public void onLoadFinished(final CachedAsyncTaskLoader<T> loader, T data); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Async task loader to fetch articles page by page. | |
* Created by hiroshi on 1/2/15. | |
*/ | |
public class PagingAsyncTaskLoader extends AsyncTaskLoader<ArticlesPage> { | |
private static final int NUM_ARTICLES_PER_PAGE = 20; | |
private boolean mIsLoading; | |
private boolean mHasError; | |
private boolean mHasMoreResults; | |
private ArticlesPage mPage; | |
public PagingAsyncTaskLoader(Context context) { | |
super(context); | |
this.init(); | |
} | |
private void init() { | |
this.mPage = new ArticlesPage(null, 0); | |
this.mHasError = false; | |
this.mHasMoreResults = true; | |
this.setLoading(true); | |
} | |
/** | |
* Called on a worker thread to perform the actual load and to return | |
* the result of the load operation. | |
* <p/> | |
* Implementations should not deliver the result directly, but should return them | |
* from this method, which will eventually end up calling {@link #deliverResult} on | |
* the UI thread. If implementations need to process the results on the UI thread | |
* they may override {@link #deliverResult} and do so there. | |
* <p/> | |
* To support cancellation, this method should periodically check the value of | |
* {@link #isLoadInBackgroundCanceled} and terminate when it returns true. | |
* Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load | |
* directly instead of polling {@link #isLoadInBackgroundCanceled}. | |
* <p/> | |
* When the load is canceled, this method may either return normally or throw | |
* {@link android.os.OperationCanceledException}. In either case, the {@link android.content.Loader} will | |
* call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the | |
* result object, if any. | |
* | |
* @return The result of the load operation. | |
* @throws android.os.OperationCanceledException if the load is canceled during execution. | |
* @see #isLoadInBackgroundCanceled | |
* @see #cancelLoadInBackground | |
* @see #onCanceled | |
*/ | |
@Override | |
public ArticlesPage loadInBackground() { | |
this.setLoading(true); | |
if (this.hasMoreResults()) { | |
try { | |
final ArticlesResponse response = service.list(this.mPage.getNextToken(), NUM_ARTICLES_PER_PAGE); | |
if (response != null && response.getArticles() != null) { | |
List<Article> articles = response.getArticles(); | |
if (articles.isEmpty()) { | |
// Empty list indicates there is no more articles | |
this.mHasMoreResults = false; | |
return null; | |
} else { | |
return new ArticlesPage(articles, response.getNextToken()); | |
} | |
} | |
} catch (Exception e) { | |
// just logs error | |
Timber.w(e, "Failed to load the articles."); | |
} | |
this.mHasError = true; | |
this.mHasMoreResults = false; | |
} else { | |
Timber.d("No following articles. Do nothing."); | |
} | |
return null; | |
} | |
@Override | |
public void deliverResult(ArticlesPage data) { | |
this.setLoading(false); | |
if (data != null) { | |
if (this.mPage.getArticles() == null) { | |
this.mPage.setArticles(data.getArticles()); | |
} else { | |
this.mPage.getArticles().addAll(data.getArticles()); | |
} | |
this.mPage.setNextToken(data.getNextToken()); | |
} | |
if (this.isStarted()) { | |
super.deliverResult(new ArticlesPage(this.mPage)); | |
} | |
} | |
@Override | |
protected void onStartLoading() { | |
if (this.mPage.getArticles() != null) { | |
// If we already have results and are starting up, deliver what we already have. | |
this.deliverResult(null); | |
} else { | |
this.forceLoad(); | |
} | |
} | |
@Override | |
protected void onStopLoading() { | |
this.setLoading(false); | |
this.cancelLoad(); | |
} | |
@Override | |
protected void onReset() { | |
super.onReset(); | |
this.onStopLoading(); | |
this.init(); | |
this.mPage.setArticles(null); | |
} | |
public boolean hasError() { | |
return this.mHasError; | |
} | |
public boolean hasMoreResults() { | |
return this.mHasMoreResults; | |
} | |
public boolean isLoading() { | |
return this.mIsLoading; | |
} | |
private void setLoading(final boolean loading) { | |
this.mIsLoading = loading; | |
} | |
public void refresh() { | |
this.reset(); | |
this.startLoading(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment