Android Loader 异步加载详解二:探寻Loader内部机制

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/70259914
本文出自【赵彦军的博客】

Android Loader 异步加载详解一:基础概念
Android Loader 异步加载详解二:探寻Loader内部机制

在上一篇文章中,讲解了 Loader 的基本概念。这一篇将会用实战的方式来探寻 Android Loader的内部机制。我们准备做一个 读取手机短信的例子。废话不多说,直接上效果图:

所有的代码都在:https://github.com/zyj1609wz/Loader

实例源码

  • 首先 SmsActivity 的源码
package com.app.loader.sms;import android.app.LoaderManager;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import com.app.loader.R;public class SmsActivity extends AppCompatActivity {private int loaderId = 0 ;private ListView lv;private SimpleCursorAdapter adapter;private LoaderManager.LoaderCallbacks loaderCallbacks ;private EditText editText ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_sms );Log.e( "loader", "activity  onCreate: ");lv = (ListView) findViewById( R.id.listview );adapter = new SimpleCursorAdapter(this,R.layout.sms_listview_item ,null,new String[]{"address","body"},new int[]{R.id.address, R.id.body},SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);lv.setAdapter(adapter);loaderCallbacks = new MyCallback() ;//初始化,并且创建Loader 实例,并且开始执行getLoaderManager().initLoader( loaderId , null,  loaderCallbacks );editText = (EditText) findViewById( R.id.editText );findViewById( R.id.start).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//开始查询String tag = editText.getText().toString() ;Bundle bundle = new Bundle();bundle.putString("key", tag );getLoaderManager().restartLoader( loaderId , bundle, loaderCallbacks  );}});findViewById( R.id.init).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//初始化,并且创建Loader 实例,并且开始执行getLoaderManager().initLoader( loaderId , null,  loaderCallbacks );}});}class MyCallback implements LoaderManager.LoaderCallbacks<Cursor> {@Overridepublic Loader onCreateLoader(int id, Bundle args) {SmsLoader loader = new SmsLoader( SmsActivity.this , args);Log.e( "loader", "onCreateLoader: ");return loader ;}@Overridepublic void onLoadFinished(Loader<Cursor> loader, Cursor data) {Log.e( "loader", "onLoadFinished: ");adapter.changeCursor( data );}@Overridepublic void onLoaderReset(Loader loader) {Log.e( "loader", "onLoaderReset: ");//当 Activity OnDestory() , 系统会回调这个方法adapter.swapCursor(null);}}@Overrideprotected void onResume() {Log.e( "loader", "activity onResume: ");super.onResume();}@Overrideprotected void onPause() {Log.e( "loader", "activity onPause: ");super.onPause();}@Overrideprotected void onDestroy() {Log.e( "loader", "activity onDestroy: ");super.onDestroy();}
}
  • activity_sms 布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context="com.app.loader.MainActivity"><EditText
        android:id="@+id/editText"android:layout_width="match_parent"android:layout_height="50dp"android:hint="请你输入过滤关键词"/><Button
        android:id="@+id/start"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="开始查询"/><Button
        android:id="@+id/init"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="开始执行 init方法"/><ListView
        android:id="@+id/listview"android:layout_width="match_parent"android:layout_height="match_parent"></ListView></LinearLayout>
  • SmsLoader 类继承 AsyncTaskLoader
package com.app.loader.sms;import android.content.AsyncTaskLoader;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;/*** Created by ${zhaoyanjun} on 2017/4/19.*/public class SmsLoader extends AsyncTaskLoader<Cursor> {private Bundle bundle;private Uri uri = Uri.parse("content://sms");private String []colums = {"_id","address","body"};public SmsLoader(Context context , Bundle bundle ) {super(context);this.bundle = bundle;}@Overridepublic Cursor loadInBackground() {String selection = null;String[] selectionArgs = null;if (bundle!=null) {selection = "body like ? ";selectionArgs = new String[]{"%"+bundle.getString("key")+"%"};}Cursor cursor = getContext().getContentResolver().query(uri, colums, selection  , selectionArgs, null);Log.e( "loader", "loadInBackground: ");return cursor;}@Overrideprotected void onStartLoading() {super.onStartLoading();forceLoad();Log.e( "loader", "onStartLoading: ");//这里一定要执行 forceLoad(); 否则 Loader 不会正常工作// onStartLoading()  --> forceLoad --> 调用 AsyncTaskLoader 里面的 forceLoad()// --> 开始创建 AsyncTask 对象实例,并且运行 AsyncTask 的 doInBackground --> SmsLoader 类中 loadInBackground()// --> 开始把结果 回调给主线程 AsyncTask onPostExecute()  -->  AsyncTaskLoader  dispatchOnLoadComplete()// -- AsyncTaskLoader deliverResult() 把最后的结果回调给 LoaderManager.LoaderCallbacks}
}

测试

  • 第一次 Activity 启动 Log 日志分析 :
com.app.loader E/loader: activity  onCreate:
com.app.loader E/loader: onCreateLoader:
com.app.loader E/loader: onStartLoading:
com.app.loader E/loader: loadInBackground:
com.app.loader E/loader: onLoadFinished: 

可以看到 第一次调用 initLoader() 方法后

 //初始化,并且创建Loader 实例,并且开始执行getLoaderManager().initLoader( loaderId , null,  loaderCallbacks );

首先 调用 LoaderCallbacks 中的 onCreateLoader 来创建一个 Loader 对象,然后调用 SmsLoader 中的 onStartLoading方法。然后调用 SmsLoader 中的 loadInBackground 开始执行 异步任务。最后在 LoaderCallbacksonLoadFinished 方法中回调。

  • 点击 “开始执行 Init 方法” 按钮,Log 分析
    点击 “开始执行 Init 方法”按钮后,开始执行
getLoaderManager().initLoader( loaderId , null,  loaderCallbacks );

Log 日志为:

com.app.loader E/loader: onLoadFinished: 

这里只回调了 onLoadFinished 的方法,把异步操作产生的数据给传递出来。请注意,这里没有走 loadInBackground 方法,说明此时 onLoadFinished 回传的数据,是旧数据,也就是上一次异步产生的数据。

但是有时我们想丢弃旧数据然后重新开始创建一个新Loader,这可怎么办呢?别担心,要丢弃旧数据调用restartLoader()即可。

  • restartLoader 方法探究。

点击 开始查询按钮 ,会执行下面的代码

//开始查询
String tag = editText.getText().toString() ;
Bundle bundle = new Bundle();
bundle.putString("key", tag );
getLoaderManager().restartLoader( loaderId , bundle, loaderCallbacks  );

Log 日志:

com.app.loader E/loader: onCreateLoader:
com.app.loader E/loader: onStartLoading:
com.app.loader E/loader: loadInBackground:
com.app.loader E/loader: onLoadFinished: 

通过日志可以看出 restartLoader重新执行了 onCreateLoader 创建了一个新的 Loader 对象; loadInBackground 丢弃了旧数据,重新加载了新数据 , 并且回调 onLoadFinished

  • 当前 Activity 从后台到前台

在测试的时候,我发现当前 Activity 从后台到前台的时候,调用顺序如下:

com.app.loader E/loader: onStartLoading:
com.app.loader E/loader: activity onResume:
com.app.loader E/loader: loadInBackground:
com.app.loader E/loader: onLoadFinished: 

可以看到当前界面从后台到前台的过程中,Loader 会自动调异步任务,并且回调新的数据。

  • 当前 Activity 销毁

当前 Activity 销毁的时候,调用顺序如下:

com.app.loader E/loader: activity onPause:
com.app.loader E/loader: activity onDestroy:
com.app.loader E/loader: onLoaderReset: 

可以看到 LoaderCallbacksonLoaderReset 方法会回调。

onLoaderReset 方法被调用的时候,代表 这个 Loader 正在被重置,此时的数据不可用。应用程序应该在这一点上删除对Loader数据的任何引用。

比如:

  @Overridepublic void onLoaderReset(Loader loader) {Log.e( "loader", "onLoaderReset: ");//当 Activity OnDestory() , 系统会回调这个方法adapter.swapCursor(null);}

参考文档:

官方文档

使用CursorLoader执行查询任务

Android应用Loaders全面详解及源码浅析

Android之Loader介绍

Android Loader 异步加载详解二:探寻Loader内部机制相关推荐

  1. Android Loader 异步加载详解一:基础概念

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/70241844 本文出自[赵彦军的博客] Android Loader 异步加载详解 ...

  2. Android Glide图片加载框架(二)源码解析之into()

    文章目录 一.前言 二.源码解析 1.into(ImageView) 2.GlideContext.buildImageViewTarget() 3.RequestBuilder.into(Targe ...

  3. Android Glide图片加载框架(二)源码解析之load()

    文章目录 一.前言 二.源码分析 1.load() Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图片加载框架(二)源 ...

  4. Android Glide图片加载框架(二)源码解析之with()

    文章目录 一.前言 二.如何阅读源码 三.源码解析 1.with() Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图 ...

  5. Android 图片异步加载的体会,SoftReference已经不再适用

    在网络上搜索Android图片异步加载的相关文章,目前大部分提到的解决方案,都是采用Map<String, SoftReference<Drawable>>  这样软引用的方式 ...

  6. 又优化了一下 Android ListView 异步加载图片

    写这篇文章并不是教大家怎么样用listview异步加载图片,因为这样的文章在网上已经有很多了,比如这位仁兄写的就很好: http://www.iteye.com/topic/685986 我也是因为看 ...

  7. Tensorflow 2.x(keras)源码详解之第十章:keras中的模型保存与加载(详解Checkpointmd5模型序列化)

      大家好,我是爱编程的喵喵.双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中.从事机器学习以及相关的前后端开发工作.曾在阿里云.科大讯飞.CCF等比赛获得多次Top名次.现 ...

  8. echars vue 添加数据没更新_vue在使用ECharts时的异步更新和数据加载详解

    前言 最近在学习eCharts,学习到了异步更新和数据加载这一块,觉着有必要总结一下,方法以后的时候参考学习,在开始本文之前,对eCharts不熟悉的朋友们可以参考下这篇文章:下面话不多说了,来一起看 ...

  9. Android ListView异步加载图片乱序问题,原因分析及解决方案

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/45586553 在Android所有系统自带的控件当中,ListView这个控件算是 ...

最新文章

  1. 负载均衡续:万亿流量场景下的负载均衡实践
  2. html5应用测试方法,详解html5的video标签测试应用
  3. 释疑の函数POPUP_TO_CONFIRM
  4. python中repeat函数用法
  5. js二级下拉被flash档住的解决办法
  6. C++语言实现-邻接矩阵
  7. AQS功能及源码详解
  8. SQL约束脚本的用法
  9. php ci框架结构,CI框架目录结构分析
  10. python动画精灵_【python游戏编程之旅】第六篇---pygame中的Sprite(精灵)模块和加载动画...
  11. 智能城市dqn算法交通信号灯调度_强化学习在智能交通灯中的应用
  12. 【ansible/ansible-tower】
  13. C++ OpenCV人脸图像提取
  14. 10大主流3D建模技术
  15. 混沌大学--喜茶模式拷贝指南
  16. 【卡尔曼滤波原理及基本认知】
  17. 公开说说别人看不到_空间设置了权限说说所有人可见
  18. python导入文件夹下所有包_python 通过文件夹导入包的操作
  19. Windows用户远程访问内网共享文件(免费内网穿透)
  20. 黄一老师:管理者需要知道的“三多”和“三少”

热门文章

  1. oracle 9i aix 迁移,Oracle 9i 在AIX上的安装 (转)
  2. mysql profile 不记录_mysql profile使用(转)
  3. map集合遍历_java---map集合获取元素与存储元素
  4. 六十八、完成Vue项目推荐和周末游组件,并使用Ajax发起ajax请求
  5. 十三、深入Java的Scanner类
  6. ICLR 2022入选论文线上分享预告:一作解读,不容错过
  7. ICML 2021 | Option-GAI: 机器人任务太长太复杂?不妨试试层次化模仿学习
  8. Nature论文解读:深度学习助力毫秒之间识别癌细胞
  9. NAACL 2019最佳论文:量子概率驱动的神经网络
  10. 【Java基础】递归输出目录下所有文件路径