本篇博客主要为Handler-Message-looper的一些深入学习笔记。

之前学习过Handler-Message-looper的相关知识,借着AOSP的学习计划,再更加深入理解一下该方法的知识。
首先,如何理解这三者?

Handler为消息的接收者和发送者,Message为消息,当然还有一个消息队列为MessageQueue。而Looper可以理解为消息队列的控制器。

一、Looper
首先,我们来看Looper,消息控制器。
在该类中,主要方法包括prepare和loop,其中使用prepare来生成消息控制器。

1
2
3
4
5
6
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}

其作用是new一个Looper,也就是new一个消息控制器。然后将该消息控制器加入到静态变量sThreadLocal中,也就是将looper和线程进行绑定,从而和线程对应的messageQueue绑定。

1
2
3
4
5
6
7
8
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}

其中的Values为Thread的内部类,

1
2
3
4
5
6
7
8
9
10
11
12
Values() {
initializeTable(INITIAL_SIZE);
this.size = 0;
this.tombstones = 0;
}

private void initializeTable(int capacity) {
this.table = new Object[capacity * 2];
this.mask = table.length - 1;
this.clean = 0;
this.maximumLoad = capacity * 2 / 3; // 2/3
}

this.table为一个数组,该数组存储着looper数据。

注意到上面的参数quitAllowed,该参数传给消息队列,用来告知其是否可以退出。
在基本的prepare构造函数上,有两个扩展的构造函数,一个是

1
2
3
public static void prepare() {
prepare(true);
}

另一个给主线程创建消息控制器的:

1
2
3
4
5
6
7
8
9
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

Looper.loop(); 则是让Looper开始工作,从消息队列里取消息,处理消息。
该函数是一个循环,当调用mHandler.getLooper().quit()后则退出循环。

二、Handler
Handler理解为消息的处理者和发送者。
我们在编程时如果使用了

1
Handler handler = new Handler();

无参的Handler会调用

1
mLooper = Looper.myLooper();

这里实际上是将Handler和Looper相绑定,而looper则通过调用prepare方法完成和线程的绑定。

如果当前的looper为null的话,则是因为没有调用Looper.prepare。

一般如果我们在主线程的话,则直接使用

new Handler(Looper.getMainLooper())该方法将handler和主线程的looper绑定,从而获得主线程上的消息队列绑定,而该handler则是公共的,我们即可以在主线程上调用handler.post发送消息,也可以在其他线程上调用handler.post发送消息。但是其发送的消息还是到了线程对应的消息队列中,并且该handler收消息和处理消息时还是只能在对应的线程上进行。
- 从任意线程发送消息给一个looper的消息队列;
- 处理相关联的looper发过来的消息。

在其他线程中,我们在run的时候需要调用Looper.prepare用来初始化控制器。
或者可以直接使用HandlerThread,

1
2
3
4
5
6
7
8
9
10
11
12
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}

它直接帮我们完成了消息控制器的初始化和启动。
有关Handler的dispatchMessage和obtainMessage的用法,可以参考下面

三、其他
1.有关Handler和Message的使用,可以参考下面的用法,在主线程上创建Handler,获取主线程的looper以及消息队列。然后在任意线程上使用该Handler发送消息,该消息加入到主线程的消息列表上了,当looper看到MessageQueue中含有Message,就将其广播出去。该handler对象收到该消息后,调用相应的handler对象的handleMessage()方法对其进行处理。同理 当我们使用handler.post方法的时候 我们会创建包含一个runnable引用的message,然后将这个message添加到这个looper的消息队列 并且其中的run方法中的内容会在与handler相关联的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
private final Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == MSG_CLEAR_CACHE_SUCCESS) {
processAfterClearCache();
} else if (msg.what == MSG_GET_CACHE_SUCCESS) {
String cacheString = msg.obj.toString();
if (cacheString.equals("0K")) {
tvCacheSize.setText("");
} else {
tvCacheSize.setText(cacheString);
}

}
return true;
}
});

private final class ShowCacheThread extends Thread {
@Override
public void run() {
String cacheSize = "";
cacheSize = BitmapCache.getInstance().getFsCacheSize();
Message msg = handler.obtainMessage(MSG_GET_CACHE_SUCCESS, cacheSize);
handler.sendMessage(msg);
}
}

2.关于主线程,之所以ActivityThread叫主线程是因为如下,在main函数中会调用prepareMainLooper,并且调用looper启动消息的循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {
Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

Looper.loop();
}

参考资料
[1]https://greenrobot.me/devpost/android-psvm/