Android Loaders(三)实现一个Base Loader

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的类)。

 

AsyncTaskLoader

是否有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]

 

 

完成一些基础的Loader

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);

       }

}

 

 

CursorLoader

例子:

 

@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);

              }

       }

}

 

 

 

使用Loader的一些限制

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

       }

}

 

 

你可能感兴趣的:(loader)