Android开发的小伙伴们对Handler机制应该非常熟悉了,Handler机制主要用于完成两个线程间互相通信,最典型的应用就是在子线程中执行一系列任务,然后通知UI线程更新UI。主要与Handler 、 Looper 、Message 这三个类相关,其中Handler负责处理(发送,接收并处理)消息,Looper负责维护一个消息队列,并通过一个不断的循环从队列里取出消息交给Handler处理,Message就是消息本身。看过不少文章,作为菜鸟的我心中还是有一些疑问,终于抽时间好好梳理了一下,不多说,直接上问题。
为了方便描述,我们把UI线程成为主线程,把其他我们自己创建的线程,称为子线程。
问题一:Handler机制中如何将两个线程联系起来,站在线程的角度,各个线程都做了些啥,执行了哪些语句?
问题二:UI线程需要处理很多事务且不能堵塞,一个线程只有一个控制权,一个线程中sendMessage之后,处理线程的控制权会在什么时机处理发送过来的message?
问题三:与UI线程绑定的Handler的postDelay(Runnable r, Long delay)方法什么情况下会阻塞UI线程?为啥?
从问题一开始,先看一个典型的场景:子线程执行了handler.sendMessage(1),结果是主线程执行了handleMessage方法里的textView.setText();
public class MainActivity extends Activity {
private TextView textView;
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
textView.setText("Handler");
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView =(TextView) findViewById(R.id.textView);
new Thread(){
public void run() {//这里是子线程在执行
try {
Thread.sleep(1000);
handler.sendEmptyMessage(1); //
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
}
}
很多文章介绍过,handler的send相关方法和post相关方法最终都会到sendMessageAtTime( Message msg, long uptimeMillis )。我们这里重点关注线程的执行步骤,或者说线程控制权的一步一步转移,看我们handler.sendMessage(1)之后的调用链:
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//记住这个target,this就是Handler的一个对象了。。。
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
子线程执行了handler.sendMessage(1),所以以上调用都是子线程来执行的。在最后一个方法enqueueMessage()中,msg的target指向当前的Handler对象handler。
所以,例子中的子线程的控制权在以下方法中转移:sendMessage(Message msg)
--->sendMessageDelayed(Message msg, long delayMillis)
--->sendMessageAtTime(Message msg, long uptimeMillis)
--->enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
--->enqueueMessage() // 这个enqueueMessage是MessageQueue的方法。
所以子线程就是往handler相关联的MessageQueue里丢了一条Message,然后控制权会回到该线程的run()方法里,去执行handler.sendMessage(1)的下一条语句,在我们的例子里,run()方法执行完毕。子线程进入死亡状态,线程对象等待被回收。
在Handler机制中,Looper负责从MessageQueue中取出message并做相应处理。子线程既然往handler对应的MessageQueue里丢了一条Message,那肯定这MessageQueue对应的线程和对应的Looper在处理这条message。在sendMessageAtTime(Message msg, long uptimeMillis)方法中,看到所操作的消息队列mQueue,看一下这个mQueue是什么时候被赋与对象(值)或者说初始化的。
找到一个Handler的构造:
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//这里这里这里
mCallback = callback;
mAsynchronous = async;
}
例子中在为handler实例化的时候使用的无参构造直接调用了上面的构造:
public Handler() {
this(null, false);
}
因为我们是在主线程中创建的hadler, 构造方法中调用Looper.myLooper()来获取Looper对象也就自然归属于主线程,也就是说这里的Looper和MessageQueue都是与UI线程相对应的,那例子中丢到这个messageQueue里的Message也自然由UI线程去取,并在loop()方法中执行msg.target.dispatchMessage(msg),这里msg.target就是刚刚被赋予的handler,最终由UI线程执行dispatchMessage()来处理这条消息。Looper.myLooper()来获取Looper对象期间的线程对应关系后边再分析。
public static void loop() {//创建Handler的线程必然执行了这个loop(),主线程默认执行了Looper.prepare()和Looper.loop()
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {//这个是主线程对应的Looper的loop方法的话,主线程的控制权会在这个for循环里直到App退出。
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);//target 就是发送该msg的Handler对象
msg.recycleUnchecked();
}
}
来看
dispatchMessage:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//handler执行post相关的方法时,丢进来的Runnble对象会作为msg.callback,优先执行该回调
handleCallback(msg);//
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {//看到回调的的handleMessage,我们并没有在定义Handler的时候生成该回调。
return;
}
}
handleMessage(msg);//终于执行了我们在Handler定义时覆写的handleMessage了!!!!
}
}
所以,
UI线程执行了Looper 的 prepare()
和loop()并在loop的无限循环中执行了Handler的
dispatchMessage(Message msg)和
handleMessage(Message msg)。
总结:Handler对应一个线程,一个线程对应且只对应一个Looper对象,一个线程对应且只对应一个MessageQueue。(注意,没有说一个线程对应一个handler)
在发送消息阶段,Handler sendMessage可以在任意线程发出,但是发出去的message会被入队到该发出消息的Handler对应的线程关联的消息队列。
在消息的处理阶段,也是由发出消息的Handler对应的线程通过Looper.loop()方法来处理消息。
到这里就可以从逻辑上解释,为什么我们自己新建的线程必须执行Looper.prepare()和Looper.loop()方法才能创建handler,因为不执行Looper.prepare(),handler发送的message不知道入队到哪个消息队列,发送消息无从谈起,不执行Looper.loop(),线程就没有不断地从消息队列取消息并处理的过程,处理消息也就无从谈起。
当然,其实Handler提供了可以传入Looper参数的构造,子线程可以在不执行Looper.prepare()和Looper.loop()方法的情况下通过传入一个Looper对象创建handler,这时Looper对应的线程即是该handler对应的线程。所谓Looper对应的线程由其prepare()方法的调用线程决定。看Looper的prepare()方法:
public static void prepare() {
prepare(true);
}
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));
}
prepare()方法通过ThreadLocal给执行prepare的线程绑定了一个 Looper对象。
ThreadLocal提供一种线程的变量管理机制,主要是set(Tvalue)和get(T value)方法,通过调用sThreadLocal.set(T value)可以为调用线程设置一个变量,这里就是给执行prepare的线程设置了一个looper对象,也是通过这个机制,保证了线程与Looper的一对一关系。上文提到的Looper.myLooper()获取的looper对象就是通过这个机制获取的。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
通过sThreadLocal.get()就可以获取当前线程调用set方法时设置的值(对象),ThreadLocal保证了不同的线程调用get时,得到的变量都是该线程set的值(对象)。
到这里就可以解答问题二了,在一个线程发送message后,对应的处理线程必须执行Looper.loop(),它的控制权就一定在loop()方法中不断循环,才能及时从消息队列中取出消息做相应的处理。
问题三,先说答案:UI线程绑定的Handler的postDelay(Runnable r, Long delay)方法delay多长时间都不会阻塞UI线程,但是r的run()方法如果执行时间过长,会阻塞。
参考文章:
《线程的私家小院:ThreadLocal》点击打开链接
鸿洋大神的:点击打开链接
《Android中的Handler解析》点击打开链接