AsyncTask是执行后台线程的最简单方式,但它不适用于那些重复且长时间运行的任务。

1. Looper

Android中,线程拥有一个消息队列(message queue),使用消息队列的线程叫做消息循环(message loop)。消息循环会循环检查队列上是否有新消息。

消息循环由线程和looper组成,Looper对象管理着线程的消息队列。

主线程就是个消息循环,因此也拥有Looper,主线程的所有工作都是由其looper完成的,looper不断的从消息队列中抓去消息,然后完成消息指定的任务。

2. Message

消息是Message类的一个实例,它有好几个实例变量,其中有三个需要在实现时定义。

1.What:用户定义的int型消息代码,用来描述消息。

2.obj:随消息发送的用户指定对象。

3.target: 处理消息的Handler。

3. Handler

Message的目标(target)是Handler类的一个实例,Handler可看作message handler的简称,创建Message时,它会自动与一个handler相关联,message待处理时,Handler对象负责触发消息处理事件、

Handler不仅仅是处理Message的目标,也是创建和发布Message的接口。

4. 关系

Looper拥有Message收件箱,所以Message必须在Looper上发布或处理。为与Looper协同工作,Handler总是引用着它。

一个Handler仅与一个Looper相关联,一个Message也仅于一个目标Handler(也称作Message目标)相关联。Looper拥有整个Message队列,多个Message可以引用同一目标Handler。

多个Handler也可与一个Looper相关联,这意味着一个Handler的Message可能与另一个Handler的Message存放在同一消息队列中。

5. 使用Handler

通常不需要手动设置消息的目标Handler。创建信息时,调用Handler.obtainMessage()方法。当传入其他消息字段给他时,该方法会自动设置目标给Handler对象。

为避免创建新的Message对象,Handler.obtainMessage()方法会从公共循环吃获取消息。  

一旦取得Message,就可以调用sendToTarget()方法将其发送给它的Handler,然后Handler会将这个Message放置在Looper消息队列的尾部。

Looper取得消息队列中的特定消息后,会将它发送给消息目标去处理。消息一般是在目标的Handler.handleMessage()实现方法中进行处理。

6. HandlerThread

HandlerThread类帮我们完成了建立looper的过程,只要继承它就能省去一些工作。

public class ThumbnailDownloader<T> extends HandlerThread {
private static final String TAG = "ThumbnailDownloader";
private static final int MESSAGE_DOWNLOAD = 0; //标识下载请求
private Boolean mHasQuit = false;
private Handler mRequestHandler; //存储对Handler的引用,这个Handler负责在ThumbnailDownloader后台线程上管理下载请求消息队列。这个Handler也负责从消息队列里取出并处理下载请求消息。 //onLooperPrepared()在Looper首次检查消息队列之前调用的。
@Override
protected void onLooperPrepared(){
mRequestHandler = new Handler(){
@Override
public void handleMessage(Message msg){ //队列中的下载消息取出并可以处理时,就会触发调用Handler.handleMessage()方法。
//处理操作
}
};
} public void queueThumbnail(T target, String url) {
//当传入其他消息字段给它时,该方法会自动设置目标给Handler对象(obtainMessage)
//sendToTarget()方法将Message发送给它的Handler,然后Handler会将这个Message放置在Looper消息队列的尾部。
mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD,target).sendToTarget();

public void clearQueue(){
mRequestHandler.removeMessages(MESSAGE_DOWNLOAD);
} }

主线程中这样调用:

mThumbnailDownloader = new ThumbnailDownloader<>(responseHandler);
mThumbnailDownloader.start();
mThumbnailDownloader.getLooper(); //在start()方法之后调用getLooper()方法是一种保证线程就绪的处理方式。可以避免潜在竞争。
// 在需要的时候调用
mThumbnailDownloader.queueThumbnail(holder, url);

7.线程交互

主线程现在能够适时调用这个线程的方法,用于下载图片了。但是还存在一个问题,那就是下载线程下载完一个任务以后如何更新视图呢?我们知道 UI 只能在主线程里更新,所以我们采用在主线程里声明一个 Handler,传递给下载线程,让下载线程在下载完成后在主线程执行更新操作。因为不能直接引用主线程的方法,故而在这里用到了回调。

7.1下载线程中:

// ThumbnailDownloader,也就是下载线程中

// 成员声明
private Handler mResponseHandler;
private ThumbnailDowloadListener<T> mThumbnailDownloadListener; // 回调接口
public interface ThumbnailDowloadListener<T> {
/*
* 图片下载完成,可以交给UI去显示时,接口中的方法就会被调用。
* 会使用这个方法把处理已下载图片的任务代理给另一个类(PhotoGalleryFragment),这样ThumbnailDownloader就可以把下载结果传给其他视图对象。
*/
void onThumbnailDownloaded(T target, Bitmap thumbnail);
} public void setThumbnailDownloaderListener(ThumbnailDowloadListener<T> listener) {
mThumbnailDownloadListener = listener;
} // 通过构造函数传递主线程的 Handler
public ThumbnailDowloader(Handler responseHandler) {
super(TAG);
mResponseHandler = responseHandler;
}

这样,主线程通过调用这些方法,就能够让下载线程获取到主线程的 Handler 和回调接口实例。

7.2主线程中

// 成员声明
private ThumbnailDowloader<PhotoHolder> mThumbnailDownloader; // 传递实例给下载线程
// 这个 Handler 在主线程中建立,所以是和主线程 Looper 相关联的
Handler responseHandler = new Handler();
mThumbnailDownloader = new ThumbnailDowloader<>(responseHandler);
mThumbnailDownloader.setThumbnailDownloaderListener(
new ThumbnailDowloader.ThumbnailDowloadListener<PhotoHolder>() {
@Override
public void onThumbnailDownloaded(PhotoHolder target, Bitmap thumbnail) {
Drawable drawable = new BitmapDrawable(getResources(), thumbnail);
target.bindDrawable(drawable);
}
}
);

8.线程交互

现在,通过 mResponseHandler,下载线程能够访问与主线程 Looper 绑定的 Handler。同时,还有 ThumbnailDownloadListener 使用返回的 Bitmap 执行 UI 更新操作。具体来说, 就是通过 onThumbnailDownloaded 实现,使用新下载的 Bitmap 来设置 PhotoHolder 的 Drawable。 
和在下载线程上把下载图片的请求放入消息队列类似,我们也可以返回定制 Message 给主线程,要求显示已下载图片。不过,这需要另一个 Handler 子类,以及一个 handleMessage(…) 覆盖方法。方便起见,我们转而使用另一个方便的 Handler 方法——post(Runnable)。

mResponseHandler.post(new Runnable() {
@Override
public void run() {
if (mRequestMap.get(target) != url ||
mHasQuit) {
return;
} mRequestMap.remove(target);
mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
}
});

在这里,新建的 Runnable 对象会被当成 Message 的回调方法,直接执行 run() 方法,所以相当于发送一个消息,里面写明了怎么做,而不是把对象和消息类型发给 Handler,让 Handler 决定怎么做。

最新文章

  1. 启动Tomcat服务器报错
  2. Java Lambda表达式入门
  3. firs tday
  4. golang的验证码相关的库
  5. Ajax 缓存问题
  6. hdu 5652 India and China Origins 二分+bfs
  7. js注册检测 用户名、密码、手机号、邮箱
  8. SQLServer:无法生成 SSPI 上下文(Cannot generate SSPI context)
  9. 【视频编解码&#183;学习笔记】5. NAL Unit 结构分析
  10. ROS常用三維機器人仿真工具Gazebo教程匯總
  11. Maven学习(七)-- 使用Maven构建多模块项目
  12. 用Verilog语言实现一个简单的MII模块
  13. gerrit中mysql配置
  14. Im4java 操作 ImageMagick 处理图片
  15. ES5-ES6-ES7_Generator 函数
  16. MySQL InnoDB引擎B+树索引简单整理说明
  17. 《Inside C#》笔记(六) 属性、数组、索引器
  18. php 对象教程
  19. pycharm 对数据库进行可视化操作
  20. SQLdeveloper同时显示多个表的窗口

热门文章

  1. 8.windows-oracle实战第八课 --管理权限和角色
  2. MySQL 基础知识梳理
  3. [WC2010]重建计划(长链剖分+线段树+分数规划)
  4. Gre封装
  5. Linux虚拟机添加硬盘
  6. MRP运算报错-清除预留
  7. RDD(三)——transformation_value类型
  8. 01-电子商城项目介绍及ssm框架搭建
  9. RadixSort(基数排序)原理及C++代码实现
  10. 77)PHP,将session数据写到不用的存储介质中