Looper-Handler-Message

本文主要通过源码分析Android中实现消息通信机制之一的Handler,了解其中实现原理和实现过程。
Android中的异步加载有2种方式:(1)Handler+Message+Looper实现异步加载,更新UI;(2)利用AsyncTask异步加载类(抽象类)。

一、 Handler+Message+Looper基础


Handler+Meaasge+Looper是android中的一种消息处理机制。因为android在子线程中是不能更新UI的,所以需要通过消息传递通知更新UI。
(1)Handler:处理消息(只处理由自己发出的消息)和发送消息,将子线程中需要传递的消息数据,通过Message封装,再用Handler将消息加入消息队列MessageQueue(一个线程可以有多个Handler);
(2)Looper:Looper就是一个循环工作的线程,负责创建一个MessageQueue,然后进入一个无线循环体不断的从该MessageQueue中读取消息。(一个线程实例中只能有一个Looper(它是一个ThreadLocal),一个Looper实例也只有一个MessageQueue)。
三者之间的关系:
(1)Looper中有一个Message队列,里面存储的是一个个待处理的Message.
(2)Message中有一个Handler,这个Handler是用来处理Message的。

1. Looper:线程循环工作

若要将一个线程创建为Looper线程,只需要在作如下修改,在线程的run方法中添加Looper.prepare()方法和Looper.loop()方法。

1
2
3
4
5
6
7
8
9
10
11
12
public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// ...其他处理,如实例化handler
// 开始循环处理消息队列
Looper.loop();
}
}

而Android的UI线程在Activity启动时就已经调用了Looper.prepare()和Looper.loop()方法,所以一般我们都是直接创建Handler实例就可以进行消息的传递。

1
2
3
4
5
6
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// 取出消息,更新UI…
}
}

1.1 Looper类源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Looper {
// 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Looper内的消息队列
final MessageQueue mQueue;
// 当前线程
Thread mThread;
// 。。。其他属性
// 每个Looper对象中有它的消息队列,和它所属的线程
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
// 我们调用该方法会在调用线程的TLS中创建Looper对象
public static final void prepare() {
if (sThreadLocal.get() != null) {
// 试图在有Looper的线程中再次创建Looper将抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
// 其他方法
public static final void loop() {...}
//结束一个looper循环
public void quit(){...}
}

其中最重要的就是prepare()方法和loop()方法:
(1) prepare()方法
在该方法中先判断sThreadLocal是否为空,这个保证了在一个线程中只能包含一个Looper实例。若sThreadLocal不为空,则将一个Looper的实例放入sThreadLocal中。
在new Looper()方法中创建一个MessageQueue消息队列。
(2) loope()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void loop() {
//获得Looper实例
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获得Looper创建的MessageQueue队列
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
//处理消息
msg.target.dispatchMessage(msg);
...
//释放资源
msg.recycle();
}
}

其中myLooper()返回sThreadLocal存储的Looper实例:

1
2
3
public static Looper myLooper() {
return sThreadLocal.get();
}

获取Looper中的MessageQueue消息队列,再通过for循环获取Message,使用msg.target.dispatchMessage(msg)把消息发给msg的target(其实就是Handler)的dispatchMessage()方法去处理,最后释放资源。

总结:Looper的作用在于与当前线程进行绑定,这样保证一个线程只有一个Looper实例,同时一个Looper实例也只能创建一个MessageQueue消息队列;loop方法不断地从MessageQueue消息队列中获取消息,再交给消息的target的dispatchMessage()方法来处理消息。

2. Handler:异步消息处理

在Looper中已经创建了一个MessageQueue并且可获取到了消息,那Looper内的消息从哪里来的呢?loop方法中调用target.dispatchMessage()方法又是怎么样处理消息的呢?Handler的作用开始体现了,我们都是直接实例化Handler:

1
2
3
4
5
6
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
// 取出消息,更新UI…
}
};

Handler类的源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class handler {
final MessageQueue mQueue; // 关联的MQ
final Looper mLooper; // 关联的looper
final Callback mCallback;//一个回调用的类
....// 其他属性
public Handler() {
....//其它代码
// 默认将关联当前线程的looper
mLooper = Looper.myLooper();
// looper不能为空,即该默认的构造方法只能在looper线程中使用
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 重要!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
mQueue = mLooper.mQueue;
mCallback = null;
}
// 其他方法
}

(1)Looper和Handler关联
其中Handler和Looper通过Looper.myLooper()进行关联,再获取Looper的MessageQueue这样就和Handler进行关联。
(2)在android应用中,我们创建Handler之后,将需要传递的消息数据用Message进行封装,再通过Handler的sendMessage()方法或者sendEmptyMessageDelayed()方法将消息发送出去,发出去之后怎么将消息存入MessageQueue呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

Handler发送消息之后,最后到enqueueMessage() 方法中将消息存储到MessageQueue里。

1
2
3
4
5
6
7
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

msg.target = this就标记了在Looper中所用到的target为Handler。
进一步queue.enqueueMessage方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
final boolean enqueueMessage(Message msg, long when) {
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
}
else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
}
...//
}

从代码中可以看出,从Handler发出来的消息是按照时间存储到MessageQueue中的。
(3)Looper中的msg.target.dispatchMessage(msg)方法
先看方法dispatchMessage(),注意这里执行的过程是存在优先级的(从上到下)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 如果message设置了callback,即runnable消息,处理callback!
handleCallback(msg);
} else {
// 如果handler本身设置了callback,则执行callback
if (mCallback != null) {
/* 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法. */
if (mCallback.handleMessage(msg)) {
return;
}
}
// 如果message没有callback,则调用Handler的子类去处理
handleMessage(msg);
}
}

最后调用了handleMessage()方法,该方法是子类中进行消息处理必须重写的方法。
总结(梳理Looper->Handler):
(1)Looper.prepare()在线程中准备一个Looepr实例,并在该实例中创建一个MessageQueue消息队列(有且只有一个);
(2)Looper.loop()让当前线程进入一个无线循环,不断从MessageQueue中读取信息,让后回调msg.target.dispatchMessage()方法;
(3)Handler的构造方法会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue相关联;
(4)Handler的sendMessage方法,会给msg的target赋值为handler自身,然后将消息加入MessageQueue队列中;
(5)在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法,在此对消息进行处理。

二、AsyncTask基础

  1. 构建AsyncTask子类的参数
    AsyncTask是一个抽象类,通常被继承实现。继承AsyncTask需要制定如下三个泛型参数:
    (1) Params:启动任务时输入参数的类型,该参数是一个varags,比如String…,相当于String[]
    (2) Progress:后台任务执行中返回值的类型,该参数是一个varags,比如String…,相当于String[]
    (3) Result:后台任务执行完成后返回的结果类型

  2. 构建AsyncTask子类的回调方法
    (1) doInBackground:必须重写,异步执行后台线程将要完成的任务(耗时操作处理)(Second
    (2) onPreExecute:执行后台耗时操作前需要调用,通常用户完成一些初始化操作(First
    (3) onPostExecute:当doInBackground()完成后,系统会自动调用onPostExecute,并将doInBackground方法返回的值传给该方法(Fourth
    (4) onProgressUpdate:在doInBackground()方法中调用publishProgress()方法更新任务的执行进度后,就会触发该方法(Third

  3. 使用步骤
    (1) 创建一个AsyncTask的子类,对象创建时带3个参数(参数1,参数2,参数3);
    (2) 调用对象的execute()时,启动异步加载进程,执行doInBackground()的代码。execute()中所传递的参数类型在参数1中描述;
    (3) 若异步加载需要对某些进行初始化处理,可以在onPreExecute()方法中进行,注意的是这些代码都是在UI线程中执行的;
    (4) 执行完异步加载后,需要进行某些处理,例如停止某些UI动画、进度条消失等等,可以重新onPostExecute()方法,同样这些代码都是在UI线程中执行的。该方法将doInBackground()方法的返回值作为onPostExecute()参数,参数类型如参数3描述。
    (5) 在执行异步加载的过程中,若需要先UI线程报告(返回值)某个处理状态,可以通过publishProgress()方法来触发,这样在UI主线程中将执行重写后的onProgressUpdate()的代码,其中传递的参数类型如参数2描述。

注意;正确使用AsyncTask的准则:
1) Task的实例必须在UI 线程中创建;
2) execute方法必须在UI 线程中调用;
3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法,需要在UI线程中实例化这个task来调用;
4) 该task只能被执行一次,否则多次调用时将会出现异常。

小例子:异步记载网络图片(进度条+ImageView)。

  1. 在doInBackground方法中
    String url = “网络图片的地址”;
    Bitmap bitmap = null;
    URLConnection connection;
    InputStream is;
    connection = new URL(url).openConnection();
    BufferedInputStream bis = new BuferedInputStream(is);
    bitmap = BitmapFactory.decodeStream(bis);
  2. 在onPreExecute方法中显示进度
  3. 在onPostExecute方法中返回得到的Bitmap图,再隐藏进度。