笔记: Loader 加载器

参考 Loader
源码分析
自定义Loader

设计目的

为了在ActivityFragment中更加方便地异步加载数据.

注意: 实际上Loader类并不提供异步功能, 真正提供异步加载功能的是它的直接子类AsyncTaskLoader.

AsyncTaskLoader是一个抽象类, 并不能直接使用, 官方仅提供了一个查询数据库或者ContentProvider的类, 就是CursorLoader类, 它直接继承了AsyncTaskLoader.

因此, 实际使用中, 如果你想实现异步加载数据, 一般是继承AsyncTaskLoader, 然后在AsyncTaskLoader#loadInBackground()中查询数据.

特点

1. 在ActivityFragment中使用, 绑定生命周期

ActivityFragment中都有getLoaderManager()方法返回一个LoaderManager, 需要注意的是, Activity自己单独持有一个LoaderManager实例, 而每一个Fragment都会持有一个实例. 而这些实例会保存在一个ArrayMap中.

绑定生命周期的是LoaderManager的实现类LoaderManagerImpl, 它有一系列对应生命周期的方法, 例如在Activity#onStart()会间接调用LoaderManagerImpl#doStart(), 在这些方法中还会调用LoaderInfo的对应方法, 然后LoaderInfo根据当前的状态来调用Loader的方法或者回调接口.

2. 异步加载数据

上面已经说过, 异步加载数据是AsyncTaskLoader的特性, 如果你直接继承Loader是不能异步加载数据的.

3. 监控数据源

监控数据源是由LoaderManagerImpl$ForceLoadContentObserver实现的.

注意: 这个类没有在LoaderAsyncTaskLoader中使用, 而是在CursorLoader中用到因此这个特点仅仅属于CursorLoader.

这里个人觉得是一个奇怪的设计, ForceLoadContentObserver继承的是android.database.ContentObserver, 而Loader明显不是仅仅针对数据库的, 或者这个类放在CursorLoader中更加合适.

4. 重建加载器时避免重新查询已经加载的数据

因为Loader的实例是单独存放在LoaderManager中了, 当Activity或者Fragment在因系统设置改变而重创建的时候LoaderManager不会被回收, 因此Loader也能够被重复利用.

注意, 如果是Activity被回收的情况, 那么LoaderManager也会被回收.

LoaderManager

作用是在Activity/FragmentLoader之间建立联系, 跟随Activity/Fragment的生命周期会有相关方法被调用, 因此Loader具有绑定声明周期的特点.

获取实例

直接通过getLoaderManager()获取实例, ActivityFragment获取的过程稍有不同, 但是最后都会得到一个LoaderManagerImpl实例.

LoaderManagerImplLoaderManager的唯一子类, ActivityFragment中持有的都是LoaderManagerImpl引用而不是LoaderManager, LoaderManager更多的意义是提供给使用者的接口.

创建Loader

通过LoaderManager#initLoader()申请创建一个Loader, 内部会根据ID判断加载器是否已经存在, 如果存在则会重复使用, 如果不存在就会触发调用LoaderManager.LoaderCallbacks#onCreateLoader()方法创建加载器.

LoaderManager不是直接引用Loader的, 而是通过LoaderInfo包装Loader, LoaderInfo同时负责保存LoaderManager.LoaderCallbacks回调接口和当前Loader的状态和数据.
LoaderInfo负责根据当前状态调用LoaderManager.LoaderCallbacks中合适的回调方法.

如果是在Activity#onStart()之前创建的Loader会等到该方法的时候统一启动, 即间接调用Loader#start(). 如果是在之后创建, 则会马上启动.

创建的Loader之后就会和Activity/Fragment的生命周期关联起来.

LoaderManager的实际作用就是把当前的生命周期传递给LoaderInfo, Loader的状态判断逻辑和接口回调都是由LoaderInfo决定.

LoaderInfo

负责保存Loader状态和直接处理LoaderManager.LoaderCallbacks.

start()

启动方法, 会在Activity#onStart()时被调用, 或者当LoaderonStart()之后初始化, 那么初始化时也会被被调用.

关键工作:

  1. 如果Loader还未创建, 那么会调用LoaderCallbacks#onCreateLoader()创建实例
  2. Loader注册接口, 让Loader在加载数据完成或者取消加载时可以通知LoaderInfo
  3. 调用了Loader#startLoading(), 通知Loader开始加载数据

stop()

LoaderManagerImpl#doStop()中被调用, 应该会被Activity#onStop间接调用.

关键工作:

  1. 重置了mStarted标志位
  2. 取消在start()中给Loader注册的接口
  3. 调用了Loader#stopLoading, 通知Loader停止加载数据

remain(), reportStart(), finishRetain()

根据生命周期处理Loader状态和回调接口, 暂略过.

destroy()

主要工作:

  1. 如果Loader已经传递过数据, 那么调用LoaderCallbacks#onLoaderReset()来通知client数据失效
  2. 取消在start()中给Loader注册的接口
  3. 调用了Loader#reset, 通知Loader重置数据

onLoadCanceled

start()中给Loader注册的OnLoadCanceledListener接口方法.

Loader加载过程中被取消时通过注册的接口方法通知LoaderInfo.

对于AsyncTaskLoader来说就是在AsyncTask#onCancelled()中调用该方法.

LoaderInfo中, Loader被取消时, 如果mPendingLoader不为null的话, 会把LoaderInfo实例移出集合, 然后destroy()自己, 接着开始加载mPendingLoader

mPendingLoader : 当外部调用LoaderManager#restartLoader()的时候, 如果当前指定的Loader仍在加载数据, 那么就会创建一个新的Loader赋值给mPendingLoader, 而不会再次启动这个Loader.

onLoadComplete

start()中给Loader注册的OnLoadCompleteListener接口方法.

Loader加载完成的时候通过这个方法通知LoaderInfo

同上, 如果mPendingLoader不为null的话, 会把LoaderInfo实例移出集合, 然后destroy()自己, 接着开始加载mPendingLoader.

另外如果Loader从未加载数据或者加载了新数据, 那么最后会调用LoaderCallbacks.onLoadFinished(), 把数据传递给client.

自定义Loader

LoaderInfo直接操作的是Loader, 会在生命周期的不同阶段调用Loader中的对应方法, 例如startLoading(), reset(), stopLoading()等等.

在这些方法里面都会有对应的protect void onXXX()方法, 例如onStartLoading(), Loader的子类就是通过重写这些方法来进行特定的逻辑.

因此, 自定义Loader需要重写其中的onXXXX()方法. 各个方法代表的含义建议查看源码注释.

另外, 在上面已经提到, LoaderInfo会给Loader注册两个接口, Loader应该在完成加载或者取消加载数据时通过这两个接口通知LoaderInfo.

因此, 自定义Loader还需要在完成加载时调用OnLoadCompleteListener#onLoadComplete, 在取消加载时调用OnLoadCanceledListener#onLoadCanceled.

AsyncTaskLoader

在实际使用中, 我们不太可能直接继承Loader, 而是继承AsyncTaskLoader. 因为它已经实现了异步加载, 同时也处理了上述提到的两点, 这可以简化自定义Loader的代码.

现在对AsyncTaskLoader稍作分析:

onForceLoad()

AsyncTaskLoader重写了Loader#onForceLoad(), 看注释可以知道:

onForceLoad()会被forceLoad()调用, 后者会在请求异步加载数据时调用, 和startLoading()的区别在于, 无论有无旧数据, forceLoad()都会加载数据. 这个方法会在主线程被调用

上面的分析我们可以知道onStartLoading()才是数据加载的发起点, 所以在这里我们可以知道

AsyncTaskLoader自身没有发起加载数据, 因为它没有重写onStartLoading()方法. 因此子类需要重写onStartLoading()来启动加载数据.

onForceLoad中, AsyncTaskLoader会执行一个AsyncTask, 因此

AsyncTaskLoader是通过AsyncTask实现异步加载的.

AsyncTaskAsyncTaskLoader会根据加载数据的情况调用OnLoadCompleteListener#onLoadComplete或者OnLoadCanceledListener#onLoadCanceled, 因此通知LoaderInfo的工作AsyncTaskLoader已经帮我们处理好了.

对应AsyncTask, AsyncTaskLoader提供了loadInBackground()onCanceled()方法来让子类可以在后台线程加载数据和在任务被取消时作处理.

onCancelLoad()

AsyncTaskLoader还重写了Loader#onCancelLoad(), 看方法名就知道在这里应该取消加载数据.
在这个方法里面, 主要就是处理AsyncTask, 根据AsyncTask所处的不同状态处理, 如果AsyncTask正在运行, 那么AsyncTaskLoader#cancelLoadInBackground()会被调用.

这是因为对于AsyncTaskLoader, 启动加载数据的工作是交给子类的, 而子类应该在后台线程中加载数据, 也就是AsyncTaskLoader#loadInBackground()中加载, 那意味着AsyncTaskLoader并不知道自己启动的后台线程做了什么, 所以提供这个方法来让子类可以取消在后台进行的工作.

因此, AsyncTaskLoader的子类需要在loadInBackground()中加载数据, 另外还需要在cancelLoadInBackground()中进行对应的操作来取消加载数据.

注意在子类中区分onCanceled()cancelLoadInBackground().
onCanceled()AsyncTask被取消时被调用的, 只是取消了某一个Task, 有可能是子类发起了另一个Task.
cancelLoadInBackground()系统想取消加载数据时用来取消loadInBackground()中的操作的, 此时整个Loader都会停止加载数据.

CursorLoader

CursorLoaderAsyncTaskLoader的直接子类, 期望返回一个Cursor实例.

因为AsyncTaskLoader仅负责处理后台线程和回调LoaderInfo注册的接口, 所以CursorLoader实现了onReset(), onStartLoading()onStopLoading()来启动/停止/重置加载数据.

另外还实现了AsyncTaskLoaderloadInBackground, cancelLoadInBackgroundonCanceled().

loadInBackground()

在后台线程中, 主要工作是通过ContentResolver来获取Cursor实例.

因此, CursorLoader是通过ContentResolver#query来获取Cursor实例的.

注意: query()可以通过一个CancellationSignal实例来取消操作.

cancelLoadInBackground()

因为在loadInBackground()中启动了一个query()操作, 所以在这里需要取消这个query()操作, CursorLoader是通过CancellationSignal实例来实现的.

onCanceled(Cursor)

简单的调用Cursor#close关闭.

总结

  1. 主要还是关注LoaderManagerLoader和生命周期的关系
  2. LoaderInfo是直接管理Loader的类, 负责根据状态调用Loader的方法.
  3. 如果希望实现异步加载则继承AsyncTaskLoader而不是直接继承Loader. 注意AsyncTaskLoader不会启动加载数据.
  4. 对于CursorLoader, 这是一个专门用作获取Cursor的类, 用来配合ContentProvider使用, 在我们自定义异步加载Loader的时候可以参考这个类的实现方式.

你可能感兴趣的:(笔记: Loader 加载器)