Loader,AsyncTaskLoader,CursorLoader与LoaderManager

2024-05-14 00:32

本文主要是介绍Loader,AsyncTaskLoader,CursorLoader与LoaderManager,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

        参考

        在实际项目,一般很少直接在主线程中进行数据库操作。为解决该问题可以使用AsyncQueryHandler,该类中有一系列的startXXX方法,可以在子线程对数据库进行CRUD操作。但如果我们加载的数据不在数据库中时,AsyncQueryHandler就显得无能为力了。此时可以使用Loader。

        Loader设计用于从数据源加载某类数据(如对象)。数据源可以是磁盘、数据库、ContentProvider、网络或者另一个进程。loader可以在不阻塞主线程的情况下获取并发送数据给接收者。android提供了Loader,AsyncTaskLoader与CursorLoader三种内置类型。

        作为基类,Loader本身没有多大用处,它定义了供LoaderManager与loader通讯的api

        AsyncTaskLoader是一个抽象的Loader。它使用AsyncTask将数据加载任务转移到其他线程上。我们用的大部分都是它的子类。

        CursorLoader,它是AsyncTaskLoader的子类,它借助ContentResolver从ContentProvider中加载数据。

        以上内容摘自王明发译的《Android编程权威指南》

Loader

        整个Loader类中没有进行任何关于加载数据的处理逻辑,它只是定义了Loader的三个状态——start,abandon,reset——以及两个监听器(OnLoadCompleteListener,OnLoadCanceledListener)和一个ContentObserver。

        ContentObserver在Loader中只是定义,在CursorLoader中才使用。CursorLoader#loadInBackground中有:

                    cursor.registerContentObserver(mObserver);

        因此,到cursor发生变化时,会执行ForceLoadContentObserver#onChange(),它又会调用onContentChanged()方法:

 public void onContentChanged() {if (mStarted) {//内容发生变化, 并且loader已经start过,所以直接强制重新加载一次forceLoad();} else {// loader已经停止了, 所以不需要立即加载新数据。<span style="font-family: Arial, Helvetica, sans-serif;">但记录下数据变化的标识,在重新启动时去刷新.</span>mContentChanged = true;}}
        这里就可以看出mContentChanged的作用:记录loader停止后数据是否发生了变化,为了在再启动时能去刷新数据。
        对于commitContentChanged(),注释上有一句话:Call this when you have completely processed a load without it being cancelled。这就是说:当你完全处理了一个没有被取消的loader时调用该方法。因此,mProcessingChange表示是否正在处理changed
    public void commitContentChanged() {mProcessingChange = false;}
        同样takeContentChanged的作用也很好说了:获取在停止时是否有内容变化。
    public boolean takeContentChanged() {boolean res = mContentChanged;mContentChanged = false;mProcessingChange |= res;//如果有变化,那就表示正在处理变化。return res;}

        Loader为三种状态提供了一系列的方法:如获取方法isStarted(),isAbandoned(),isReset()。修改方法如下:

    public final void startLoading() {//禁止私自调用//修改三个状态,略onStartLoading();}protected void onStartLoading() {}public boolean cancelLoad() {return onCancelLoad();}protected boolean onCancelLoad() {return false;}/*** 强制重新加载数据。无论是否已经加载过数据* 必须在主线程中调用。*/public void forceLoad() {onForceLoad();}protected void onForceLoad() {}/*** 由LoaderManager调用,禁止自己调用。* 停止向客户端反应最新的数据变化,但依旧能监听到。*/public void stopLoading() {mStarted = false;onStopLoading();}/*** 自己处理stop loading的逻辑的地方*/protected void onStopLoading() {}/*** 由LoaderManager调用,禁止私自调用。* 保留现有数据,但不会通知新数据变化*/public void abandon() {mAbandoned = true;onAbandon();}protected void onAbandon() {}/*** 同上面stop一样*/public void reset() {onReset();//修改状态,略}protected void onReset() {}
        方法很多,只有forceLoad与cancelLoad能调用,其余的都是LoaderManager调用的。
        从上面可以看出,Loader只是定义了一些操作自身的函数,方便LoaderManager使用。它并没有真正加载数据,一般加载数据都是通过继承AsyncTaskLoader实现的

LoaderManager

        LoaderManager主要用来管理loader。可以在activity/fragment中调用getLoaderManager()获取LoaderManager的实例。LoaderManager只是一个接口,它的真正实现是其内部类<LoaderManagerImpl。

initLoader()

        通过该方法可以完成loader的初始化。LoaderManager会根据loader的状态自动调用第三个参数(以下称回调)中的方法。这也是回调的作用:根据loader的状态,执行不同的操作。

        第一个参数是指定当前loader的id,这是该loader的唯一标识。当id相同时,loader就相同。

        第二个参数是方便在初始化loader时传递参数。它最终会传递到回调中onCreateLoader()中成为第二个参数。

        initLoader()的部分代码如下:

	public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {……LoaderInfo info = mLoaders.get(id);//mLoaders是SparseArray对象,所以key值可以为int类型的……if (info == null) {// Loader doesn't already exist; create.info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);} else {info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;}if (info.mHaveData && mStarted) {// If the loader has already generated its data, report it now.info.callOnLoadFinished(info.mLoader, info.mData);}return (Loader<D>)info.mLoader;}
        其中mLoaders存储的是当前所有激活的loader实例(These are the currently active loaders)。

        最后一个判断表明:如果当前的loader有数据,并且已经启动,就会直接调用LoadInfo.callOnLoadFinished(),而在该方法中会调用到LoaderCallbacks.onLoadFinished()

        当info为null时调用createAndInstallLoader(),同时将initLoader()中的所有参数传递进去。createAndInstallLoader()主要代码如下:

            LoaderInfo info = createLoader(id, args, callback);installLoader(info);
        再看一下createLoader()的代码
    private LoaderInfo createLoader(int id, Bundle args,LoaderManager.LoaderCallbacks<Object> callback) {LoaderInfo info = new LoaderInfo(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);//分别为LoaderInfo中的mId,mArgs,mCallbacks赋值Loader<Object> loader = callback.onCreateLoader(id, args);//调用onCreateLoader生成Loader实例info.mLoader = (Loader<Object>)loader;//将生成的实例赋值到LoaderInfo#mLoaderreturn info;}
        首先创建LoaderInfo,并调用回调中的onCreateLoader创建Loader。同时为info中的mLoader赋值。

        在installLoader()中,会调用mLoaders.put(info.mId, info),这里的info.mId就是initLoader()中的第一个参数。这样就将新建的loader存储到了mLoaders中。

        同时installLoader()还会调用info.start()。这是一个很重要的方法 ,因为它会调用Loader.startLoading()。代码为:

	void start() {……mStarted = true;……if (mLoader == null && mCallbacks != null) {//再一次判断,防止mLoader为nullmLoader = mCallbacks.onCreateLoader(mId, mArgs);}if (mLoader != null) {……if (!mListenerRegistered) {mLoader.registerListener(mId, this);mLoader.registerOnLoadCanceledListener(this);mListenerRegistered = true;}mLoader.startLoading();}}
        当mLoader不为null时,会调用mLoader.startLoading,同时为mLoader设置监听器。再结合createLoader()来看,mLoader的值就是LoaderCallbacks.onCreateLoader()的返回值。

        从这里开始已经由LoaderManager进入到了Loader中。

restartLoader

        当使用initLoader()时,如果指定的id的loader已经存在,系统会重用该loader;否则系统会创建一个新的。但是如果想丢弃掉旧的loader,应该使用restartLoader()。

AsyncTaskLoader

        异步执行加载操作。
        从上面的LoaderManager#initLoader()中可以看出,最后会执行的是onStartLoading。但AsyncTaskLoader并没有重写这个方法,而是重写了onForceLoad。由Loader的分析发现,onForceLoad()的调用是由forceLoad()引起的。因此,需要重写Loader#onStartLoading并调用forceLoad()这一点可以从CursorLoader中得到佐证。如下:
    protected void onStartLoading() {//CursorLoader.javaif (mCursor != null) {deliverResult(mCursor);}if (takeContentChanged() || mCursor == null) {forceLoad();//调用forceLoad(),并在loadInBackground()进行查询操作。}}

onForceLoad

    @Overrideprotected void onForceLoad() {//调用forceLoad会执行到该方法super.onForceLoad();//方法空实现cancelLoad();mTask = new LoadTask();executePendingTask();}
        LoadTask为AsyncTask的子类。最后一个方法如下:
    void executePendingTask() {if (mCancellingTask == null && mTask != null) {……mTask.executeOnExecutor(mExecutor, (Void[]) null);//启动AsyncTask进行异步执行}}

        在mTask的doInBackground中会调用onLoadInBackground(),它又调用loadInBackground(),这才是异步执行的地方。

        mTask为AsyncTask,故加载完毕后会走onPostExecute中,又会调用dispatchOnLoadComplete(),其作用主要是对加载结果进行分发。如下:

void dispatchOnLoadComplete(LoadTask task, D data) {if (mTask != task) {//Load complete of old task, trying to cancel.mTask只有在onForceLoad()中被赋值,所以这里的操作就是旧有的loader加载完数据dispatchOnCancelled(task, data);} else {if (isAbandoned()) {// loader被废弃,所以给个回调,让子类有机会处理data的回收。参考CursorLoader.onCanceled(data);} else {//通知数据变化,以及分发数据commitContentChanged();//将Loader中的mProcessingChange设置为falsemLastLoadCompleteTime = SystemClock.uptimeMillis();mTask = null;//关闭当前的loaderdeliverResult(data);//很简单,只是调用Loader中关联的OnLoadCompleteListener回调}}}
        在LoaderInfo#start()中,为Loader设置的OnLoadCompleteListener是LoaderInfo自身,因此上面的deliverResult会执行到LoaderInfo#onLoadComplete().有如下代码
if (mData != data || !mHaveData) {mData = data;mHaveData = true;if (mStarted) {callOnLoadFinished(loader, data);//这里最终会调用到Callback中的onLoadFinished}}

        至此,Loader的加载过程结束。这中间唯一需要自己操作的就是继承AsyncTaskLoader时,必须重写onStartLoad()并在其中调用forceLoad(),不然整个加载过程不完整。

总结

        LoaderManager通过id区分它所管理的loader。当两个loader的id相同时,便会被认为是同一个loader。

        如果当前id的loader已经存在,那么不会创建新的loader,只是更新initLoader()中的回调。

        如果当前id的loader已经存在,并且已经有数据,那么不会再次查询数据,而是直接将数据传递到回调中的onLoadFinished()中。

示例

        一个完整的对AsyncTaskLoader的实现,有些东西基本上是固定的,而且与CursorLoader类似。例子来源于google的TODO-MVP-Loaders。如下:

public class TasksLoader extends AsyncTaskLoader<List<Task>>implements TasksRepository.TasksRepositoryObserver{private TasksRepository mRepository;public TasksLoader(Context context, @NonNull TasksRepository repository) {super(context);checkNotNull(repository);//如果repository为null,此句话抛异常mRepository = repository;//为加载数据的数据源。根据实际逻辑换。}@Overridepublic List<Task> loadInBackground() {//异步加载数据return mRepository.getTasks();}@Overridepublic void deliverResult(List<Task> data) {if (isReset()) {//重置过,加载的数据就不需要。这里还需要对数据进行回收处理。CursorLoader中这里会关闭cursorreturn;}if (isStarted()) {//start了,将新加载的数据传递给loader。最终会走到LoaderCallbacks#onLoadFinishedsuper.deliverResult(data);}//这里一般需要对旧数据进行一些回收处理。}@Overrideprotected void onStartLoading() {if (mRepository.cachedTasksAvailable()) {//有缓存,就先把缓存给返回deliverResult(mRepository.getCachedTasks());}mRepository.addContentObserver(this);//takeContentChanged()的判断是一定有的if (takeContentChanged() || !mRepository.cachedTasksAvailable()) {//该loader停止后有数据变化,或者别的什么原因必须进行更新(本例中是缓存不可用),就强制更新数据。forceLoad();//它会调用到loadInBackground()。}}@Overrideprotected void onStopLoading() {//CursorLoader也这么调,没考虑为啥。cancelLoad();}@Overrideprotected void onReset() {onStopLoading();////进行一些清尾工作mRepository.removeContentObserver(this);}//TasksRepository.TasksRepositoryObserver中的回调,跟Loader无关@Overridepublic void onTasksChanged() {if (isStarted()) {forceLoad();}}
}






这篇关于Loader,AsyncTaskLoader,CursorLoader与LoaderManager的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/987240

相关文章

Android-Universal-Image-Loader三大组件DisplayImageOptions、ImageLoader、ImageLoaderConfiguration详解 一、介绍

一、介绍  Android-Universal-Image-Loader是一个开源的UI组件程序,该项目的目的是提供一个可重复使用的仪器为异步图像加载,缓存和显示。所以,如果你的程序里需要这个功能的话,那么不妨试试它。因为已经封装好了一些类和方法。我们 可以直接拿来用了。而不用重复去写了。其实,写一个这方面的程序还是比较麻烦的,要考虑多线程缓存,内存溢出等很多方面。 二、具体使用 一个好的类库的

Linux+WebLogic11g:java.lang.LinkageError: loader constraint violation in interface itable initializa

在项目的WEB-INF目录下,有如下weblogic.xml文件 [html]  view plain  copy <?xml version="1.0" encoding="UTF-8"?>     <weblogic-web-app         xmlns="http://www.bea.com/ns/weblogic/weblogic-web-app"

webpack解析字体,file-loader

注意:安装之前,先保证webpack项目能正常运行! 因为字体和图片都不是代码文件,所以,都可以使用file-loader 1.安装 file-loader 依赖 cnpm i file-loader -D 2.在src目录下创建font文件夹,并引入字体 (这里引入AdobeGothicStd-Bold.otf.otf ) 3.配置,在 webpack.config.js 下新增配置:

手写 webpack loader

webpack loader (webpack 加载器) webpack => web pack 1)转换ES6语法成ES5 2)处理模块加载依赖 3)生成一个可以在浏览器加载执行的 js 文件 loader (引用定义) 1、是 webpack 用于在编译过程中解析各类文件格式,并输出; 2、本质上就是一个 node 模块,通过写一个函数来完成自动化的过程; 3、由此我们就可以在开发模式下,通

mcu loader升级固件原理与实现

1 mcu loader升级固件原理         mcu 固件有两部分,如下图所示,一部分是 loader.bin,一部分是 app.bin,将两部分的固件合并在一起烧录进 mcu 的 flash 当中。mcu 上电进入loader 模式执行 loader.bin 部分的程序,然后读取 flash 某个地址的值,判断是否进入 app 模式执行app.bin 部分的程序。         用

ios打包版本构建神器《Application loader》3.6版本在哪里下载?

在上传app构建版本的时候,我们可以看到官方极力推荐Application loader。 1、什么是Application loader?

【PyYaml】yaml.load()时总是出现警告:YAMLLoadWarning: calling yaml.load() without Loader=...

警告提示:YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details.   YAML 5.1版本后弃用了yaml.load(file)

Webpack4 自定义 Loader

1.Loader Loader 就像是一个翻译员,能把源文件经过转化后输出新的结果,并且一个文件还可以链式的经过多个翻译员翻译。 以下面处理 CSS 文件为例: CSS 源代码会先交给 css-loader 处理,找出 CSS 中依赖的资源、压缩 CSS 等;把 css-loader 输出的 CSS 交给 style-loader 处理,转换成通过脚本加载的 JavaScript 代码( J

Webpack4 配置TS Loader

TypeScript 是 JavaScript 的一个超集,主要提供了类型检查系统和对 ES6 语法的支持,但不支持新的 API。 目前没有任何环境支持运行原生的 TypeScript 代码,必须通过构建把它转换成 JavaScript 代码后才能运行。 TypeScript 官方提供了能把 TypeScript 转换成 JavaScript 的编译器:typescript 。 你需要在当前项目

java.lang.UnsatisfiedLinkError: Couldn't load libjniFramework from loader

原文地址:http://blog.csdn.net/wangbaochu/article/details/47842295 今天开发jni的项目,一切编译好之后,启动App遇到如下错误: [java]  view plain  copy libjniFramework.so load error:java.lang.UnsatisfiedLinkError: Couldn