NOTE: 以下路径相对于android源码下
以下类容主要介绍,线程中消息处理及循环
〇.关系图
为了便于理解,从网络上摘录的关系流程图,这些图在看完代码再来看比较好。
一、ThreadLocal
线程局部存储(ThreadLocalStorage)当从同一线程中引用该变量时,其值总是相同;而从不同的线程中引用该变量时,其值应该不同。
方法成员变量:仅在方法内部有效
类成员变量:仅在对象内部有效
线程局部存储(TLS)变量:在本线程内的任何对象内保持一致
静态变量:在本进程内的任何对象内保持一致
跨进程通信(IPC)变量:一般使用 Binder 定义,在所有进程中保持
源码:libcore/luni/src/main/java/java/lang/ThreadLocal.java
编译后放在out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/java/lang/ThreadLocal.java
ThreadLocal类提供了2个关键函数:
set: 设置调用线程的局部变量
get: 获取调用线程的局部变量
set/get结果值和调用这个函数的线程有关
本例中,用set/get来保存和获取当前线程的Looper对象
二、Looper
./frameworks/base/core/java/android/os/Looper.java
作用:
1、调用该类中的静态方法 prepare() 创建一个消息队列
在 Looper 的静态方法 prepare() 中,给线程局部存储变量中添加一个新的 Looper 对象:
- public static final void prepare() {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper());
- }
Looper 的构造方法中创建了一个 MessageQueue 对象:
- private Looper() {
- mQueue = new MessageQueue();
- mRun = true;
- mThread = Thread.currentThread();
- }
2、提供静态方法 loop() ,使调用该方法的线程进行无限循环,并从消息队列中读取消息
- 1 public static final void loop() {
- 2 Looper me = myLooper();
- 3 MessageQueue queue = me.mQueue;
- 4 while (true) {
- 5 Message msg = queue.next(); // might block
- 6 //if (!me.mRun) {
- 7 // break;
- 8 //}
- 9
- 10 if (msg != null) {
- 11 if (msg.target == null) {
- 12 // No target is a magic identifier for the quit message.
- 13 return;
- 14 }
- 15 if (me.mLogging!= null) me.mLogging.println(
- 16 ">>>>> Dispatching to " + msg.target + " "
- 17 + msg.callback + ": " + msg.what
- 18 );
- 19
- 20 msg.target.dispatchMessage(msg);
- 21
- 22 if (me.mLogging!= null) me.mLogging.println(
- 23 "<<<<< Finished to " + msg.target + " "
- 24 + msg.callback);
- 25
- 26 msg.recycle();
- 27 }
- 28 }
- 29 }
1)调用 myLooper() 函数返回当前线程的 Looper对象,即调用 sThreadLocal.get() 方法返回当前线程 ID 对应的 Looper 对象
- public static final Looper myLooper() {
- return (Looper)sThreadLocal.get();
- }
2)通过 Looper 对象获取线程的消息队列
- MessageQueue queue = me.mQueue;
3)进入 while 无限循环
(1)调用MessageQueue 对象的 next() 方法取出队列中的消息(如果当前队列为空,则当前线程会被挂起,也就是说 next() 内部会暂停当前线程)
- Message msg = queue.next();
(2)回调 dispatchMessage(msg) 完成对消息的处理。msg 变量的类型是 Message, msg.target 的类型是 Handler
- msg.target.dispatchMessage(msg);
(3)处理完消息后,调用 msg.recycle() 回收 Message 对象占用的系统资源。因为 Message 类内部使用了一个数据池保存 Message 对象,从而避免不停的创建和删除 Message 对象,因此每次处理完该消息后需要将 Message 对象表明为空闲状态
对程序员来讲,当需要把一个线程变为异步消息处理线程时,应该在 Thread 类的 run() 方法中先调用 Looper.prepare() 为该线程创建一个 MessageQueue 对象,然后再调用 Looper.loop() 方法,使当前线程进入消息处理循环,当然发送消息线程发送消息也是调用同一个mHandler来sendMessage(msg)
基本框架如下:
{
//...
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare(); //普通线程接收消息必要步骤一,prepare(),创建了MessageQueue
mHandler = new Handler() { //要重写handleMessage()函数,用于loop()中调用handlerMessage()进行回调处理发送过来的消息
public void handleMessage(Message msg) {
// process incoming messages here
//....
}
};
Looper.loop();//普通线程接收消息必要步骤二,loop(),进入消息处理循环
}
}
private class WorkerThread extends Thread {
//...
@Override
public void run() {
String sdPath = Environment.getExternalStorageDirectory().getPath();
Message msg = new Message();
//...
mHandler.sendMessage(msg);
}
}
//...
}
三、MessageQueue
./frameworks/base/core/java/android/os/MessageQueue.java
1、消息在 MessageQueue 中以 Message 表示,消息以链表的结构进行保存
2、MessageQueue 中两个主要的方法“取出消息”和“添加消息”,这个一般由Handler中的方法通过MessageQueue 方法间接操作
1)取出消息 next()
- final Message next() {
- ...
- // Try to retrieve the next message, returning if found.
- synchronized (this) {
- now = SystemClock.uptimeMillis();
- Message msg = pullNextLocked(now);
- if (msg != null) return msg;
- if (tryIdle && mIdleHandlers.size() > 0) {
- idlers = mIdleHandlers.toArray();
- }
- }
- ...
- }
2)添加消息 enquenceMessage()
- final boolean enqueueMessage(Message msg, long when) {
- ...
- synchronized (this) {
- ...
- msg.when = when;
- //Log.d("MessageQueue", "Enqueing: " + msg);
- Message p = mMessages;
- if (p == null || when == 0 || when < p.when) {
- msg.next = p;
- mMessages = msg;
- this.notify();
- } else {
- Message prev = null;
- while (p != null && p.when <= when) {
- prev = p;
- p = p.next;
- }
- msg.next = prev.next;
- prev.next = msg;
- this.notify();
- }
- }
- return true;
- }
四、Handler
./frameworks/base/core/java/android/os/Handler.java
1、尽管 MessageQueue 提供了直接读/写的函数接口,但对于应用程序员而言一般不直接读/写消息队列。在Looper.loop() 函数中,当取出消息后回调 msg.target 的 handleMessage() 方法,而 msg.target 的类型正是 Handler。
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
我们一般用 Handler 类向消息队列中发送消息,并重载 Handler 类的 handleMessage() 函数添加消息处理代码。
2、Handler 对象只能添加到有消息队列的线程中,否则发生异常
- mLooper = Looper.myLooper();
- if (mLooper == null) {
- throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
- }
因此,在构造 Handler 对象前必须已经执行过 Looper.prepare() ,但 Looper.prepare() 不能被执行两次。
一个线程或者说一个looper,可以有多个Handler,比如一个主线程接收多个worker线程发来的消息,多个worker线程可以使用同一个Handler来发送消息(当然也只能在一个Handler接收处理),也可以不同worker线程使用不同Handler来发送(当然是由对应的Handler来接收处理)
五、UI线程和自定义 Thread的区别
1、UI线程
UI线程是从 ActivityThread 运行的,在 ActivityThread 中的 main() 方法中已经使用 Looper.prepareMainLooper() 为线程添加了 Looper 对象(即已经创建了消息队列MessageQueue),如下代码片:
public static void main(String[] args) {
SamplingProfilerIntegration.start();
<span style="white-space:pre"> </span>//...
Looper.prepareMainLooper();//调用了prepare()即new 一个Looper对象,这个Looper构造函数中创建了MessageQueue
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();//调用函数Loop,用于UI线程中进行循环消息处理
throw new RuntimeException("Main thread loop unexpectedly exited");
}
2、自定义 Thread
自定义 Thread 是一个裸线程,没有Looper,也就没有MessageQueue,因此不能直接在 Thread 中定义 Handler对象(new Handler时会获取当前线程的Looper对象),也就不能处理Message。