基本概念
Handler
Handler
主要用于异步消息的处理。
与 Looper
沟通,Push
新消息到 MessageQueue
里,或者接收处理 Looper
从 MessageQueue
里取出的消息。
Message
进行消息的封装。
Message
类中的关键变量:
- public int what :
用于声明此Message
的类型。 - public int arg1 :
public int arg2 :
用于传递一些整型数据。 - public Object obj :
用于传递一个对象。
Message
类中的关键接口方法:
- public static Message obtain() :
优先从一个全局的消息池中返回一个新的Message
(复用消息池找那个的消息)。很多情况下可以避免新生成一个消息的额外开销。(尽管可以通过New Message()
获得一个新的消息对象,但是建议优先使用obtain()
获得一个空的Message
,以节约资源)
Message msg = Message.obtain();
- public Handler getTarget() :
获取到处理此消息的Handler
对象。 - public void setData(Bundle data) :
用于传递一个Bundle
对象。对应的使用getData()
或peekData()
取出此Bundle
。(注意:如果只是传递简单的int
信息,应优先使用arg1
,arg2
)
MessageQueue
被 Looper
持有,用来保存消息(Message
)。
消息队列是先进先出的。
消息不会直接添加到 MessageQueue
,而是通过 Handler
对象和 Looper
。
可以通过 Looper.myQueue()
来获取当前线程的 MessageQueue
对象。
Looper
Looper
字面意思就是循环者。它被设计用来使一个普通线程变成 Looper
线程,也就是可以循环工作的线程。
Looper
内部维护了一个 MessageQueue
消息队列。
例如:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare(); //创建Looper对象
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop(); //循环处理消息队列
}
}
调用 quit()
方法结束 looper
循环。
Looper
有以下几个要点:
- 每个线程有且只能有一个
Looper
对象,它是一个ThreadLocal
。 -
Looper
内部有一个消息队列,loop()
方法调用后线程开始不断从队列中取出消息执行。 -
Looper
使一个线程变成Looper
线程。
ThreadLocal
线程本地变量。
每个 Thread
内部有一个 ThreadLocal
。
ThreadLocal
不是一个线程而是一个线程的本地化对象。当工作于多线程环境中的对象采用 ThreadLocal
维护变量时,ThreadLocal
为每个使用该变量的线程分配一个独立的副本。每个线程都可以独立的改变自己的副本,而不影响其他线程的副本。
ThreadLocal
类中的接口方法:
- public void set(Object value) :
设置当前线程的线程局部变量的值。 - public Object get() :
返回当前线程的线程局部变量的值。 - public void remove() :
删除当前线程的局部变量的值。 - protected Object initialValue() :
返回当前线程局部变量的初始值。
ThreadLocal
是如何做到为每一个线程维护一份独立的变量副本的呢?
思路很简单,在 ThreadLocal
类中有一个 Map
, Map
中的键为线程对象,值为对应线程的变量副本。
ThreadLocal
与线程同步机制的比较:
线程同步机制通过对象的锁机制保证同一时间只有一个线程去访问变量,该变量时多个线程共享的。ThreadLocal
则为每一个线程提供了一个变量副本,从而隔离了多个线程访问数据的冲突,ThreadLocal
提供了线程安全的对象封装,在编写多线程代码时,可以把不安全的代码封装进 ThreadLocal
。概括的说,对于多线程资源共享的问题,线程同步机制采取了时间换空间的方式,访问串行化,对象共享化;而 ThreadLocal
采取了空间换时间的方式,访问并行化,对象独享化。
使用例子:
public class TestThreadLocal {
private ThreadLocal<Integer> mNum = new ThreadLocal<Integer>() {
public Integer initialValue() {
//设置默认值为0
return 0;
}
};
public int getNextNum() {
mNum.set(mNum.get() + 1);
return mNum.get();
}
private static class TestThread extends Thread {
private TestThreadLocal mTest;
private TestThread(TestThreadLocal testThreadLocal) {
mTest = testThreadLocal;
}
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("Thread name:" + Thread.currentThread().getName() + ",num:" + mTest.getNextNum());
}
}
}
public static void main(String [] args) {
TestThreadLocal testThreadLocal = new TestThreadLocal();
TestThread thread1 = new TestThread(testThreadLocal);
TestThread thread2 = new TestThread(testThreadLocal);
TestThread thread3 = new TestThread(testThreadLocal);
thread1.start();
thread2.start();
thread3.start();
}
}
打印
Thread name:Thread-0,num:1
Thread name:Thread-1,num:1
Thread name:Thread-2,num:1
Thread name:Thread-1,num:2
Thread name:Thread-0,num:2
Thread name:Thread-2,num:2
Thread name:Thread-0,num:3
Thread name:Thread-2,num:3
Thread name:Thread-1,num:3
Handler 的原理
Handler 原理图Handler 原理:
-
Handler
关联线程的Looper
。 -
Handler
发送消息,通过Looper
的MessageQueue
把消息插入MessageQueue
队列中。 -
Looper
不断循环,取出MessageQueue
中队头的Message
。 - 调用
Handler
的dispatchMessage
方法,让Handler
处理消息。
Handler 和 Looper
Handler
的初始化主要有两种方式:
- 未指定
Looper
方式:
Handler handler = new Handler();
通过这种方式 new
出来的 Handler
对象,默认使用当前线程的 Looper
。
源码如下:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper(); // 默认将关联当前线程的looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; //关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
mCallback = callback;
mAsynchronous = async;
}
注意:
Activity
被创建时就默认创建了 Looper
,Thread
是没有默认创建 Looper
的。
- 指定 Looper 的方式:
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper; //直接设置为传入的 Looper 对象
mQueue = looper.mQueue; //关联的也是传入的 Looper 中的 MQ
mCallback = callback;
mAsynchronous = async;
}
注意:
一个线程可以有对个 Handler
,但是只能有一个 Looper
。
Handler 发送消息
Handler
的使用会有两种方式,一种是发送 Message
、一种是发送 Runnable
。
- 发送
Message
:
这种方式有以下接口方法:
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
几个接口调用如下图:
发送消息接口可见最后都是调用的 sendMessageAtTime(Message msg, long uptimeMillis)
接口。
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//重要,这里把当前Handler设置给msg.target,方便后面从MQ中取出消息后,能让对应的Handler处理此消息
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//把消息放入MQ消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}
- 发送
Runnable
:
这种方式有以下接口方法:
post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
几个接口的调用如下图:
由上图可见,最后都会调用到 sendMessageDelayed(Message msg, long delayMillis)
接口,而 Runnable
者会封装到 Message
的 callback
变量中,而 Message
也是放入到消息队列中的。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
Looper 中对消息的处理
调用 Looper
的 loop()
方法后,Looper
线程就开始了循环工作,不断的从 MessageQueue
中取出队头的消息执行。
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//进入死循环
for (;;) {
//从消息队列取出队头的消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//关键:msg.target为发送消息的Handler,这里把取出来的msg交给Handler处理
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
Handler 中 dispatchMessage 处理消息
上面 Looper
取出消息后,会调用 Handler
的 dispatchMessage(Message msg)
接口处理消息。
public void dispatchMessage(Message msg) {
//msg.callback 为 Runnable,即Handler.post(Runnable r) 这种方式调用的
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果不是post Runnable 的方式,则为sendMessage的方式,直接会调用handleMessage(msg)处理消息
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
//post runnable 的方式,handleCallback中直接调用了Runnable的run方法
message.callback.run();
}
关于线程问题
由于 Handler
是在它关联的 Looper
线程中处理(dispatchMessage(Message msg))消息的,所以 Looper
所在的线程决定了 Handler
的处理消息的线程。
Activity
的主线程也是一个 Looper
线程,所以在主线程中创建的 Handler
,在子线程中通过 Handler
发送消息,最后处理消息时在主(UI)线程中。这也是我们平时为什么能通过 Handler
来解决非主线程中更新 UI
的问题的原因。
如何让 Handler
关联一个子线程的 Looper
,使 Handler
在子线程中处理消息?
Android HandlerThread
参考:
网友评论