loadermanager使用解析之个人见解

来源:互联网 发布:pdf.js 获取当前页数 编辑:程序博客网 时间:2024/05/17 03:22

在学习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, nullthis); //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 0
原创粉丝点击