Android Loaders(三)实现一个Base Loader
转载请注明:http://blog.csdn.net/liaoqianchuan00/article/details/24094913
参考翻译自:https://docs.google.com/presentation/d/1_5puFz6kUK1cSYvTmJbvQFYpgj8LXZECBrn65w62UKk/edit#slide=id.p
support提供了三个类:Loader(最基本的Loader抽象类),AsyncTaskLoader(抽象类,继承自Loader),C(继承自AsyncTaskLoader,是一个专门用来调用ContentProvider的类)。
是否有AsyncTask一样的使用限制?为什么提供一个基于AsyncTask的Loader呢?
不,他内部使用的是每个Android版本的实现都是一样的ModerAsyncTask而不是AsyncTask,
private staticfinal int CORE_POOL_SIZE = 5;
privatestatic final int MAXIMUM_POOL_SIZE = 128;
privatestatic final int KEEP_ALIVE = 1;
publicstatic final Executor THREAD_POOL_EXECUTOR = newThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue,sThreadFactory);
privatestatic volatile Executor sDefaultExecutor = THREAD_POOL_EXECUTOR;
重点:可以防止内存泄露
在设计Loader的时候,就只保持了一个对Applicationcontext的引用,所以v会导致内存泄露。
/** * Stores away the application contextassociated with context. Since Loaders can be * used across multiple activitiesit's dangerous to store the context directly. * * @param context used toretrieve the application context. */public Loader(Context context) {
mContext =context.getApplicationContext();
}
但是你的Loader只能是static的,不然的话他们就会保持一个对outer class的引用。
实现
我们需要继承AsyncTaskLoader,然后来实现他得行为。
1. 重要的回调:
onStartLoading()
onStopLoading()
onReset()
onForceLoad() from Loader
ORloadInBackground() from AsyncTaskLoader
2. 可选的回调
deliverResult() [override]
1. BasicLoader
public abstract class BasicLoader<T> extendsAsyncTaskLoader<T> {
publicBasicLoader(Context context) {
super(context);
}
@Override
protectedvoid onStartLoading() {
forceLoad(); // Launch the background task
}
@Override
protectedvoid onStopLoading() {
cancelLoad(); // Attempt to cancel the current load taskif possible
}
@Override
protectedvoid onReset() {
super.onReset();
onStopLoading();
}
}
2. LocalCacheLoader
public abstract class LocalCacheLoader<T>extends AsyncTaskLoader<T> {
private TmResult;
publicLocalCacheLoader(Context context) {
super(context);
}
@Override
protectedvoid onStartLoading() {
if(mResult != null) {
//If we currently have a result available, deliver it
//immediately.
deliverResult(mResult);
}
if(takeContentChanged() || mResult == null) {
//If the data has changed since the last time it was loaded
//or is not currently available, start a load.
forceLoad();
}
}
@Override
protectedvoid onStopLoading() {
//Attempt to cancel the current load task if possible.
cancelLoad();
}
@Override
protectedvoid onReset() {
super.onReset();
onStopLoading();
mResult= null;
}
@Override
publicvoid deliverResult(T data) {
mResult= data;
if(isStarted()) {
//If the Loader is currently started, we can immediately
//deliver its results.
super.deliverResult(data);
}
}
}
3. global cache
public abstract class GlobalCacheLoader<T>extends AsyncTaskLoader<T> {
...
@Override
protectedvoid onStartLoading() {
TcachedResult = getCachedResult();
if(cachedResult != null) {
//If we currently have a result available, deliver it
//immediately.
deliverResult(cachedResult);
}
if(takeContentChanged() || cachedResult == null) {
//If the data has changed since the last time it was loaded
//or is not currently available, start a load.
forceLoad();
}
}
...
protectedabstract T getCachedResult();
}
两个帮助我们的方法:
1. onContentChanged:如果Loader isstarted,那么会调用forceLoad。如果Loader is stopped:将会设置一个标志,下次takeContentChanged的时候会使用到。
2. takeContentChanged:返回contentchanged的标志,并且清楚这个标志。
例子:
public abstract class AutoRefreshLoader<T>extends LocalCacheLoader<T> {
privatelong interval;
privateHandler handler;
privatefinal Runnable timeoutRunnable = new Runnable() {
@Override
publicvoid run() {
onContentChanged();
}
};
publicAutoRefreshLoader(Context context, long interval) {
super(context);
this.interval= interval;
this.handler= new Handler();
}
...
...
@Override
protectedvoid onForceLoad() {
super.onForceLoad();
handler.removeCallbacks(timeoutRunnable);
handler.postDelayed(timeoutRunnable,interval);
}
@Override
publicvoid onCanceled(T data) {
super.onCanceled(data);
//Retry a refresh the next time the loader is started
onContentChanged();
}
@Override
protectedvoid onReset() {
super.onReset();
handler.removeCallbacks(timeoutRunnable);
}
}
例子:
@Override
publicLoader<Cursor> onCreateLoader(int id, Bundle args) {
returnnew BookmarksLoader(getActivity(),
args.getDouble("latitude"),args.getDouble("longitude"));
}
@Override
publicvoid onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);
//The list should now be shown.
if(isResumed()) {
setListShown(true);
}else {
setListShownNoAnimation(true);
}
}
@Override
publicvoid onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
如果你不需要如此复杂的ContentProvice,但是想要读取本地DB。可以参照下面的例子:
private static class BookmarksLoader extendsSimpleCursorLoader {
privatedouble latitude;
privatedouble longitude;
publicBookmarksLoader(Context context, double latitude, double longitude) {
super(context);
this.latitude= latitude;
this.longitude= longitude;
}
@Override
protectedCursor getCursor() {
returnDatabaseManager.getInstance().getBookmarks(latitude, longitude);
}
}
public class DatabaseManager {
privatestatic final Uri URI_BOOKMARKS =
Uri.parse("sqlite://your.package.name/bookmarks");
...
publicCursor getBookmarks(double latitude, double longitude) {
//A big database query you don't want to see
...
cursor.setNotificationUri(context.getContentResolver(),URI_BOOKMARKS);
returncursor;
}
...
...
publicboolean addBookmark(Bookmark bookmark) {
SQLiteDatabasedb = helper.getWritableDatabase();
db.beginTransaction();
try{
//Other database stuff you don't want to see
...
long result = db.insert(DatabaseHelper.BOOKMARKS_TABLE_NAME,null, values);
db.setTransactionSuccessful();
//Will return -1 if the bookmark was already present
returnresult != -1L;
}finally {
db.endTransaction();
context.getContentResolver().notifyChange(URI_BOOKMARKS,null);
}
}
}
1. 没有内置的进度更新(和AsyncTask比)。解决方法:使用LocalBroadcastManager。
@Override
protected void onStart() {
//Receive loading status broadcasts in order to update the progress bar
LocalBroadcastManager.getInstance(this).registerReceiver(loadingStatusReceiver,
new IntentFilter(MyLoader.LOADING_ACTION));
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(loadingStatusReceiver);
}
@Override
public Result loadInBackground() {
// Show progress bar
Intent intent = new Intent(LOADING_ACTION).putExtra(LOADING_EXTRA,true);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
try {
return doStuff();
} finally {
// Hide progress bar
intent = newIntent(LOADING_ACTION).putExtra(LOADING_EXTRA, false);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
}
}
2. 在LoaderCallbacks中没有错误处理机制。
你遇到error的时候通常只能简单的返回null。
解决方法:
1. 封装一个结果和exception。比如Pair<T,Exception>.你的Loader的cache需要更智能一些,他可以检查result是否为null或者是否有error。
2. 在Loader中加入一个exception的属性。
public abstract class ExceptionSupportLoader<T>extends LocalCacheLoader<T> {
privateException lastException;
publicExceptionSupportLoader(Context context) {
super(context);
}
publicException getLastException() {
returnlastException;
}
@Override
public TloadInBackground() {
try{
returntryLoadInBackground();
} catch(Exception e) {
this.lastException= e;
return null;
}
}
protectedabstract T tryLoadInBackground() throws Exception;
}
@Override
public void onLoadFinished(Loader<Result>loader, Result result) {
if(result == null) {
Exceptionexception = ((ExceptionSupportLoader<Result>) loader)
.getLastException();
//Error handling
} else {
//Result handling
}
}