loadermanager使用解析之个人见解

在学习loader之前,先谈谈为什么使用这个。以下是官方对于loader的介绍:

1、They are available to every Activity and Fragment. 

2、They provide asynchronous loading of data. 

3、They monitor the source of their data and deliver new results when the content changes.

4、They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to re-query their data. 

看看我做的一个demo,结合这个demo来讲讲哪些地方loader,以及关于loader的一般用法。

      首先先看看loader的简单用法:

                第一,我需要知道requestLoader在哪里创建?

                第二,何时会被促发loader的创建?

                第三,我们创建的loader,什么时候会被启动去获取数据呢?

                第四,当服务将数据返回之后,客户端怎么拿到这个数据?

一、哪里创建loader


 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public  class  SearchBookResultFragment  extends  BaseFragment  implements  LoaderManager.LoaderCallbacks<DoubanSearchBook>{
     @Override
     public  Loader<DoubanSearchBook> onCreateLoader( int  id, Bundle bundle) {
          //该方法会被LoaderManager的内部实现类LoaderMangerImp回调。该函数会根据传入的ID,初始化并返回一个自定义的loader(
         //负责处理耗时的操作,当异步线程操作完成之后,就会返回请求的数据).
         如:  return  new  RequestLoader(getActivity(),  new  GuessBookNameRuquest( "莫言" ), Request.Origin.NET);  //自定义的requestLoader
     }
     @Override
     public  void  onLoadFinished(Loader<DoubanSearchBook> resultLoader, DoubanSearchBook result) {
         //接收请求得到的数据,并进行相应的处理。
     }
     @Override
     public  void  onLoaderReset(Loader<DoubanSearchBook> resultLoader) {
        //当loader被重置时,使得数据无效.
     }
};

二、何时启动创建loader方法

 

         那么问题来了,我们创建loader的这个方法什么时候会被调用呢?看以下一块代码:

 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
     public  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
     }
     @Override
     public  View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         return  super .onCreateView(inflater, container, savedInstanceState);
     }
     @Override
     public  void  onActivityCreate(Bundle savedInstanceState) {
         super .onActivityCreated(savedInstanceState);
         getListView().setSelector(android.R.color.transparent);
         getListView().setDivider( null );
         if  (getArguments() !=  null ) {
             mSearchText = getArguments().getString(TEXT);
         }
         //获得loadermanager中的initloader方法,并将loaderCallBack接口实现类传入
         getLoaderManager().initLoader(PAGE_LOADER_ID,  null this );  //init方法如何调用创建loader,就需要了解一下LoaderManger这个抽象类
     }
     private  Exception exception;   
     @Override
     public  void  onLoadFinished(Loader<List<DoubanBookInfo>> loader, List<DoubanBookInfo> data, Exception exception) {
         super .onLoadFinished(loader, data, exception);
         this .exception = exception;
     }
     @Override
     protected  List<DoubanBookInfo> getList(List<DoubanBookInfo> doubanBookInfos) {
         return  doubanBookInfos;
     }
     @Override
     protected  void  refresh() {
         Bundle args =  new  Bundle();
         args.putBoolean( "refresh" true );
         getLoaderManager().restartLoader( 0 , args,  this );
     }

 

  • LoaderManger干嘛用的

         
          那么我们需要来了解一下LoaderManager这个抽象类是干嘛用的,它里面又包括些什么内容。

       initLoader方法源码:

 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public  <D> Loader<D> initLoader( int  id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
         if  (mCreatingLoader) {
             throw  new  IllegalStateException( "Called while creating a loader" );
         }
         
         //判断SparseArrayCompat中loaderInfo中的 loader(loaderInfo.mloader)是否存在,如果不存在去createAndInstallLoader(createLoader,installLoader))
创建loader
         LoaderInfo info = mLoaders.get(id);
         
         if  (DEBUG) Log.v(TAG,  "initLoader in "  this  ": args="  + args);
         if  (info ==  null ) {
             // 如果loader不存在,则重新创建(createAndInstallLoader) 找到了第二个问题的答案!!!
             info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
             if  (DEBUG) Log.v(TAG,  "  Created new loader "  + info);
         else  {
            //如果loader存在的话,那么只要info对象回调LoaderCallback替换为我们需要被回调的fragment或Activity
             if  (DEBUG) Log.v(TAG,  "  Re-using existing loader "  + info);
             info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
         }
         //如果已经有数据,且启用onstart方法(mStarted标记是否调用fragment或者Activity的onStart方法(注意)
         if  (info.mHaveData && mStarted) {
             // If the loader has already generated its data, report it now.
             info.callOnLoadFinished(info.mLoader, info.mData); //回调callback的onLoadFinished方法;
         }
         
         return  (Loader<D>)info.mLoader;
     }

 

           createAndInstallLoader()方法中的createLoader方法调用了callback.onCreateLoader(id, args);//调用回调方法onCreateLoader时机(第二个问题的答案)

 

 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private  LoaderInfo createAndInstallLoader( int  id, Bundle args,
         LoaderManager.LoaderCallbacks<Object> callback) {
     try  {
         mCreatingLoader =  true ;
         LoaderInfo info = createLoader(id, args, callback);  //createloader中会去调用客户端的回调createLoader方法(第二个问题答案)
         installLoader(info);(第三个问题答案...后续详谈)
         return  info;
     finally  {
         mCreatingLoader =  false ;
     }
}   
 
  private  LoaderInfo createLoader( int  id, Bundle args,
         LoaderManager.LoaderCallbacks<Object> callback) {
     LoaderInfo info =  new  LoaderInfo(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
     Loader<Object> loader = callback.onCreateLoader(id, args); //调用回调方法onCreateLoader时机
     info.mLoader = (Loader<Object>)loader;  //将loader存入SparseArrayCompat数组中
     return  info;
}
}

        综上所述,简单的理解是,LoaderManager用来负责管理与Activity或者Fragment联系起来的一个或多个Loaders对象。而真正起作用的,用来启动、停止、保持、重启、关闭它的Loaders的是LoaderManagerImpl中的LoaderInfo这个内部类。

  • getLoaderManger()方法解析

         那么了解完这个loaderManager这个类大概干嘛用之后,我们就需要知道我们怎么获取这个LoaderManager类型的实例呢;以上方法中提到了一个getLoaderManger方法来获得一个LoadermangerImple实例;接下来谈谈这个方法。看源码:

 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
  * Return the LoaderManager for this fragment, creating it if needed.
  */
public  LoaderManager getLoaderManager() {
     if  (mLoaderManager !=  null ) {  //如果存在loadermanager对象则直接返回
         return  mLoaderManager;
     }
     if  (mActivity ==  null ) {
         throw  new  IllegalStateException( "Fragment "  this  " not attached to Activity" );
     }
     mCheckedForLoaderManager =  true ;
     mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted,  true ); //不存在则会调用Activity的getLoaderManger方法,其中有个参数mWho是用来唯一标示这个fragment的
     return  mLoaderManager;
}

 

那么我们就来看看Activity中的这个方法;

 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LoaderManagerImpl getLoaderManager(String who,  boolean  started,  boolean  create) {
     if  (mAllLoaderManagers ==  null ) {
         mAllLoaderManagers =  new  SimpleArrayMap<String, LoaderManagerImpl>();  //创建一个存放Loadermanager的一个map;
     }
     LoaderManagerImpl lm = mAllLoaderManagers.get(who);   //如果在map中有存在与这个fragment对应的LoaderManagerImpl,那么直接从map中取就可以咯。(PS:每个fragment只持有一个loadermanager对象的)
     if  (lm ==  null ) { //如果之前不存在
         if  (create) {
             lm =  new  LoaderManagerImpl(who,  this , started);  //答案来了,这个时候就会去创建一个LoaderManagerImpl对象。(Magic)
             mAllLoaderManagers.put(who, lm);  //然后再把它放到map中,以便下次直接从map中取,而不需要去创建。(以who这个key存储)
         }
     else  {
         lm.updateActivity( this );
     }
     return  lm;
}

那么这个mWho这个key这么重要,它是怎么来的呢;看源码;可以知道由setIndex方法来设置这个值的。

 Collapse source
1
2
3
4
5
6
7
8
final  void  setIndex( int  index, Fragment parent) {
     mIndex = index;  //magic哈哈
     if  (parent !=  null ) {
         mWho = parent.mWho +  ":"  + mIndex;
     else  {
         mWho =  "android:fragment:"  + mIndex;
     }
}

那方法是啥时候被调用的呢,继续看源码:

 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public  Fragment instantiate(FragmentActivity activity, Fragment parent) {
        if  (mInstance !=  null ) {
            return  mInstance;
        }
        
        if  (mArguments !=  null ) {
            mArguments.setClassLoader(activity.getClassLoader());
        }
        
        mInstance = Fragment.instantiate(activity, mClassName, mArguments);
        
        if  (mSavedFragmentState !=  null ) {
            mSavedFragmentState.setClassLoader(activity.getClassLoader());
            mInstance.mSavedFragmentState = mSavedFragmentState;
        }
        mInstance.setIndex(mIndex, parent);  //答案终于找到了,就在这个,其中mIndex初始化设置为-1
        mInstance.mFromLayout = mFromLayout;
        mInstance.mRestored =  true ;
        mInstance.mFragmentId = mFragmentId;
        mInstance.mContainerId = mContainerId;
        mInstance.mTag = mTag;
        mInstance.mRetainInstance = mRetainInstance;
        mInstance.mDetached = mDetached;
        mInstance.mFragmentManager = activity.mFragments;
        if  (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
                "Instantiated fragment "  + mInstance);
        return  mInstance;
    }


三、何时去获取数据

      前面我们看到的createAndInstallLoader中会调用installLoader方法,第一次initLoader时只是创建,没有启动loader去取数据,因为mStarted为false
注意点:LoaderInfo里的mStart指的是该Loader有没有启动,LoaderManager里的mStart指的是Activity或Fragment有没有调用onStart()。所以,loader的执行一定是在Activity或Fragment的onStart()调用时。)该方法源码如下:

 

 Collapse source
1
2
3
4
5
6
7
8
void  installLoader(LoaderInfo info) { 【LoaderManager中的方法】
       mLoaders.put(info.mId, info);
       if  (mStarted) {  //mStarted值由Fragment中生命周期的onstart方法决定的。(注意这个时候onstart方法还没有被调用,所以mStart为false,不执行info.start)
           // The activity will start all existing loaders in it's onStart(),
           // so only start them here if we're past that point of the activitiy's
           // life cycle
           info.start();  //最终调用的是star方法中的Loader的startLoading方法,中的onStartLoading方法(空实现),所以自定义Loader时,需要override该方法
       }

 

Fragment的生命周期onStart方法,会调用loadermanager的doStart方法,

 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  void  onStart() {
     mCalled =  true ;
     
     if  (!mLoadersStarted) {
         mLoadersStarted =  true ;
         if  (!mCheckedForLoaderManager) {
             mCheckedForLoaderManager =  true ;
             mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted,  false );
         }
         if  (mLoaderManager !=  null ) {
             mLoaderManager.doStart();
         }
     }
}

dostart方法,就会去调用LoaderInfo的start方法了。

 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void  doStart() {
     if  (DEBUG) Log.v(TAG,  "Starting in "  this );
     if  (mStarted) {
         RuntimeException e =  new  RuntimeException( "here" );
         e.fillInStackTrace();
         Log.w(TAG,  "Called doStart when already started: "  this , e);
         return ;
     }
     
     mStarted =  true //注意,这个时候mStarted未设置为true了
     // Call out to sub classes so they can start their loaders
     // Let the existing loaders know that we want to be notified when a load is complete
     for  ( int  i = mLoaders.size()- 1 ; i >=  0 ; i--) {
         mLoaders.valueAt(i).start();
     }
}

 

接着看start方法是怎么做的:

 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void  start() {
                //重启动前,已经在执行了,只需要恢复状态即可
            if  (mRetaining && mRetainingStarted) {
                // Our owner is started, but we were being retained from a
                // previous instance in the started state...  so there is really
                // nothing to do here, since the loaders are still started.
                mStarted =  true ;
                return ;
            }
            if  (mStarted) {
                // If loader already started, don't restart.
                return ;
            }
            mStarted =  true ;
            
            if  (DEBUG) Log.v(TAG,  "  Starting: "  this );
            if  (mLoader ==  null  && mCallbacks !=  null ) {
               mLoader = mCallbacks.onCreateLoader(mId, mArgs);
            }
            if  (mLoader !=  null ) {
                if  (mLoader.getClass().isMemberClass()
                        && !Modifier.isStatic(mLoader.getClass().getModifiers())) {
                    throw  new  IllegalArgumentException(
                            "Object returned from onCreateLoader must not be a non-static inner member class: "
                            + mLoader);
                }
                if  (!mListenerRegistered) {
                    mLoader.registerListener(mId,  this );
                    mListenerRegistered =  true ;
                }
                mLoader.startLoading();
            }
        }

 

 


四、客户端如何获取数据

       
           LoaderMangerImple会调用Loader的startLoading;

 

 Collapse source
1
2
3
4
5
6
public  final  void  startLoading() {
     mStarted =  true ;
     mReset =  false ;
     mAbandoned =  false ;
     onStartLoading();
}

 

         如果数据存在,则调用delivertResult方法,将数据传递给注册的监听者(实现了)OnLoadCompleteListener(Loadermanger),接着由Loadermanager调用onLoadFinish方法将数据返回给client;数据不存在,调用forceLoad()[其中会创建一个线程去],ConcurrentTaskLoader这个继承loader的抽象类,其中会开启异步加载数据 AsyncTaskLoader中的LoadTask(继承自ConcurrentTask继承自ModernAsyncTask),数据加载的操作在doInBackground()方法中执行,该方法会调用OnloadInBackgroud(),由客户端实现。

 

 Collapse source
1
2
3
4
5
6
7
8
9
@Override
protected  void  onStartLoading() {
     if  (data !=  null ) {
         deliverResult(data);
     }
     if  (takeContentChanged() || data ==  null ) {
         forceLoad();
     }
}
 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
final  class  LoadTask  extends  ConcurrentTask<Void, Void, D>  implements  Runnable {
     D result;
     boolean  waiting;
     private  CountDownLatch done =  new  CountDownLatch( 1 );
     LoadTask() {
     }
     LoadTask(Executor executor) {
         super (executor);
     }
     /* Runs on a worker thread */
     @Override
     protected  D doInBackground(Void... params) { 
         if  (DEBUG) Log.v(TAG,  this  " >>> doInBackground" );
         result = ConcurrentTaskLoader. this .onLoadInBackground(); //调用loadInBackground()方法,这个就是我们需要操作的异步请求的任务!!!!所以客户端需要实现这个方法
         if  (DEBUG) Log.v(TAG,  this  "  <<< doInBackground" );
         return  result;
     }
     /* Runs on the UI thread */
     @Override
     protected  void  onPostExecute(D data) {     //获取到数据之后,就会调用dispatchOnLoadComplete()返回给客户端
         if  (DEBUG) Log.v(TAG,  this  " onPostExecute" );
         try  {
             ConcurrentTaskLoader. this .dispatchOnLoadComplete( this , data);  !!!
         finally  {
             done.countDown();
         }
     }
     @Override
     protected  void  onCancelled() {
         if  (DEBUG) Log.v(TAG,  this  " onCancelled" );
         try  {
             ConcurrentTaskLoader. this .dispatchOnCancelled( this , result);
         finally  {
             done.countDown();
         }
     }
     @Override
     public  void  run() {
         waiting =  false ;
         ConcurrentTaskLoader. this .executePendingTask();
     }
}

 

         执行完获得数据之后,会在onPostExecute,调用dispatchOnLoadComplete方法将数据返回给Loader->DeliverResult. (注意:网络请求返回的数据,需要进行处理,response中处理好数据之后,传给DeliverResult())
         Loadermanager中的内部类LoaderInfo类实现OnLoadCompleteListener接口(用于loader加载完数据之后调用),会在deliverResult方法中回调onLoadComlete方法 ,将数据返回给LoaderManager,LoaderManager中的onLoadComlete方法会调用callOnLoadFinished,该方法中会回调client的onLoadFinished方法,将数据传回给client。(LoaderInfo中包含loader,作为它的一个成员变量)

 

 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    void  dispatchOnLoadComplete(LoadTask task, D data) {         if  (mTask != task) {
         if  (DEBUG) Slog.v(TAG,  "Load complete of old task, trying to cancel" );
         dispatchOnCancelled(task, data);
     else  {
         if  (isAbandoned()) {
             // This cursor has been abandoned; just cancel the new data.
             onCanceled(data);
         else  {
             commitContentChanged();
             mLastLoadCompleteTime = SystemClock.uptimeMillis();
             mTask =  null ;
             if  (DEBUG) Slog.v(TAG,  "Delivering result" );
             deliverResult(data);  //调用deleverResult返回数据
         }
     }
 
}

看看deliverResult方法:

public  void  deliverResult(D data) {
     if  (mListener !=  null ) {
         mListener.onLoadComplete( this , data);     //Loadermanager中的内部类LoaderInfo类实现OnLoadCompleteListener接口,回调onLoadComplete
     }                                             //loader是在registerListener中注册的
}

 

             
  附上:

 

 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public  class  RequestLoader<D>  extends  AbstractModelLoader<D> {
     protected  final  Request.Origin origin;
     private  final  Request<D> request;
     public  RequestLoader(Context context, Request<D> request, Request.Origin origin) {
         super (context);
         this .request = request;
         this .origin = origin;
     }
     @Override
     protected  void  onStartLoading() {
         super .onStartLoading();
     }
     @Override
     protected  D doLoadData()  throws  IOException {
         return  getRequest().execute(origin);
     }
     @Override
     protected  Executor dispatchExecutor() {
         switch  (origin) {
             case  LOCAL:
                 return  ConcurrentTask.SERIAL_EXECUTOR;
             case  NET:
                 return  ConcurrentTask.THREAD_POOL_EXECUTOR;
             case  UNSPECIFIED:
             default :
                 return  getRequest().isLocalValid() ? ConcurrentTask.SERIAL_EXECUTOR : ConcurrentTask.THREAD_POOL_EXECUTOR;
         }
     }
     public  Request<D> getRequest() {
         return  request;
     }
}
 Collapse source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public  class  SearchBookResultRequest  extends  AbstractBookListRequest {
     private  static  final  String URL_PATH =  "v2/book/search" ;
     private  final  String keyword;
     public  SearchBookResultRequest(String keyword) {
         this .keyword = keyword;
     }
     @Override
     protected  List<DoubanBookInfo> local()  throws  IOException {
         return  null ; //暂不做本地化
     }
     @Override
     public  List<DoubanBookInfo> execute(Origin origin)  throws  IOException {
         return  super .execute(Origin.NET);
     }
     @Override
     public  boolean  isLocalValid() {
         return  false ;
     }
     @Override
     protected  String getBaseUrl() {
         Uri.Builder builder = Uri.parse(ApiConfig.baseDoubanApiUrl).buildUpon();
         builder.appendEncodedPath(URL_PATH)
                 .appendQueryParameter( "q" , keyword)
                 .toString();
         return  builder.toString();
     }
     @Override
     public  List<DoubanBookInfo> convert(JsonElement rootElement)  throws  IOException {
         JsonObject rootObject = rootElement.getAsJsonObject();
         int  total = rootObject.has( "total" ) ? rootObject.get( "total" )
                 .getAsInt() :  0 ;
         setTotal(total);
         return  super .convert(rootElement);
     }
 
     @Override
     protected  String getUrl() {
         Uri.Builder builder = Uri.parse(getBaseUrl()).buildUpon();
         if  (limit !=  0 ) {
             builder.appendQueryParameter( "start" , String.valueOf(offset));
             builder.appendQueryParameter( "count" , String.valueOf(limit));
         }
         return  builder.toString();
     }
}

 

 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值