Android中,使用AsyncTask来执行简单的后台线程,但是AsyncTask较适用于短暂的、不重复的运行任务。因为从Android3.2起,AsyncTask不再为每个实例创建线程,而是利用一个Executor在单一的后台线程上运行所有的AsyncTask后台任务,相当于所有的AsyncTask实例在同一个队列中排队运行,所以长时间且重复运行的AsyncTask会影响其他实例的运行,导致堵塞。实现按需下载的等后台运行方式,通常会使用到HandlerThread。
消息
首先介绍一下消息(Message),消息是Message类的实例,携带着需要处理的各种任务信息。有以下几个常见变量:
- what:用户定义的int型消息代码,用来描述和识别信息;
- obj:随消息发送的用户指定对象;
- target:处理消息的Handler,创建Message时会自动与一个Handler关联,通常不用手动设置;
- arg1、arg2:随消息返回的两个int类型参数。
Handler
Handler作为Message的目标,除了用来触发message的处理事件,也是创建和发布Message的接口。message是在Handler.handleMessage(...)方法中进行处理的。
Looper和Message、Handler的关系
Android的消息循环(message loop)由线程和looper组成。Looper对象管理着消息队列(meesage queue),消息队列中存放着许多的消息(message),它们与handler相关联。为了与Looper协同工作,Handler总是引用着Looper。主进程本身就是一个拥有handler和Looper的消息循环,在主线程中Android 默认已经调用了Looper.prepare()方法,调用该方法的目的是在Looper 中创建MessageQueue成员变量并把Looper对象绑定到当前线程中。
- 一个Message只和一个目标Handler相关联,但是多个Message能引用同一个目标Handler。(如下图左)
- 一个Handler只和一个Looper相关联,但是多个Handler可以与一个Looper相关联。所以消息队列中可能存放着来自不同Handler的Message。(如下图右)
创建Message并关联Handler
如果有一个类继承了HandlerThread,那么Handler可以在函数onLooperPrepared()中新建,因为该函数是在Looper首次检查消息队列之前调用的。一般会在new一个Handler的同时覆写其handleMessage(...)方法。
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
//TODO
}
}
};
消息的发送可以有以下几种:
- 使用Handler.obtainMessage(...)方法来创建信息,此时可以将消息自动设置目标为该Handler对象,取得Message之后可以使用sendToTarget发送给Handler。Handler会把该Message放置在MessageQueue消息队列尾部。
Message mMessage = mHandler.obtainMessage(...);
mMessage.sendToTarget();
- 使用Handler.sendMessage(...)方法:
mHandler.sendMessage(message);
- 也可以使用Handler.post()方式来发送消息:
mHandler.post(new Runnable() {
@Override
public void run() {
//TODO
}
});
看看源码会发现它是把Runnable转换成了一条空的message,设置它的私有变量callback为我们传入的Runnable,然后再调用sendMessageDelayed(...)方法的。
当message设有回调方法时,它从消息队列取出后,是不会发给target Handler的,而是直接执行存储在callback中的Runnable的run()方法。