1.问题
1.handler.post()和view.post()里面Runnable,是执行在哪个线程? 为什么?
2.handler,looper,messagequeue,thread之间的对应关系?
3.如何在子线程里面构建handler?
4.HandlerThread如何实现的?
2.日常用法
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
new Thread(()->{
handler.sendEmptyMessage(1);
}).start();
子线程向主线程发送数据,达到更新UI界面的作用,这是我第一次接触Handler的用法。
这里面最关键的问题是handler发送的message是如何到达handleMessage。
然后把这个问题分解成两个角度去分析,1.如何入队 2.如何出队
2.1 入队
从源码分析:
第一步:封装成Message
不论是handler.post(),或者handler.sendEmptyMessage()等,本质上都是和handler.sendMessage()一样,只是他们先要把输入的数据包装成一个Message,然后把message入队,而handler.sendMessage()直接输入的就是一个Message,故不需要再封装。
handler.post()源码分析:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
getPostMessage()把输入r即Runnable,赋值给message.callback,接着通过sendMessageDelayed将message发送出去。
第二步:将message加入MessageQueue
从handle.sendMessage()源码分析入列流程:
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;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
从sendMessage()和post()可以看出,两个方法其实都是通过sendMessageDelayed将Message发送出去,到最后运行enqueueMessage()函数,将message入队。其实handler的所有入队操作都相差不大,最终都是通过enqueueMessage方法将message加入到MessageQueue。
2.2 出队
在介绍出队之前,先看下在子线程中是如何创建Handler的。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyHandlerThread myHandlerThread = new MyHandlerThread();
myHandlerThread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myHandlerThread.handler.sendEmptyMessage(1234);
}
public class MyHandlerThread extends Thread{
Handler handler;
@Override
public void run() {
super.run();
Looper.prepare();
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.e(TAG,msg.what+"");
}
};
Looper.loop();
}
}
上面的代码是简单的子线程中handler的实现,在MyHandlerThread中,首先要调用Loop.perpare(),为该线程创建一个Loop,接着调用Looper.loop()进入循环中,在该循环中不断的取出Message,也就是所谓的出队操作。上述代码中的延时1秒,是为了确保在发送message之前loop已经创建完毕,毕竟这个loop是在子线程中创建的。
sLooper.loop()关键点的源码分析:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for(;;){
Message msg = queue.next(); // might block
msg.target.dispatchMessage(msg);
}
从Threadlocal中取出Looper即myLooper()。ThreadLocal的特性,使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。
接着在for循环不断取出message,queue.next是阻塞的,当没有消息时,会阻塞在这。
msg.target指的是handler,msg.target.dispatchMessage也就是调用handler处理message,从这里也可以看出来,由那个handler发送的信息也就由那个handler进行处理。
dispatchMessage()源码分析:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
先判断msg.callback是否为空,如果不为空,则进入handleCallback()进行处理,message的callback指的是Runnable,在前文也指到handler.post(),是将Post中的Runnable赋值给message的callback,于是可以得知当使用handler.post()时,是在handleCallbak()里面对message进行处理。
handlerCallback()源码分析:
private static void handleCallback(Message message) {
message.callback.run();
}
message.callback.run()也就是直接调用runnable中的run方法,(PS:开辟一个线程必须调用thread.start(),直接调用thread.run()是无法开辟一个新的线程)。于是,要看handler.post()中的Runable运行在那个线程,要看 msg.target.dispatchMessage(msg)方法运行在那个线程,而 msg.target.dispatchMessage(msg)方法是在Loop中调用,于是要看loop对应的是那个线程。view.post()中的Run able使用的handler中的Loop是UI线程的,于是view.post方法是运行在UI线程,无论view.post方法是在那个线程调用。
下面看如果msg.callback为空,进行else中,再判断mCallback是否为空,也就是handler中mCallback是否赋值。
Callback源码如下:
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
可以看出,handler的Callback是一个接口,如果自己实现handler的callback,然后再赋值给handler,则会执行msg.callback逻辑。
如果handler的mcallback也为空,则会执行handlerMessage方法,也就是我们最常用这个方法,handlerMessage是一个空方法,所以需要重写这个方法对msg进行处理,即上文的第一个例子。
总结一下:首先判断message中的runnable是否为空,如果为空再判断handler中的mcallback是否为空,如果mCallback也为空,则调用handlermessage()进行处理。(不想画图 TOT!)
其实从上文的分析,我们也可以看出,一个线程可以有多个handler,但一个线程只能有一个loop,也只能有一个messageQueue,所以多个handler对应一个loop,对应一个thread,对应一个messageQueue,即n:1:1:1。
3.HandlerThread的实现
HandlerThread handlerThread = new HandlerThread("handlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
handler.post(()->{
Log.e(TAG,Thread.currentThread().getName());
});
09-21 14:55:36.557 10068-10143/com.example.asus.spitest E/MainActivity: handlerThread
上述代码为HandlerThread的简单使用,我们可以看出handler.post中的runnable在handlerThread中运行,从此也可以看出handler中的runnable在那个线程运行与handler在那个线程创建无关,而是看handler的looper是在那个线程中创建。
handlerThread源码分析:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
handlerThread也就是在子线程中实现Handler,我们重点比较这个与上文实现的在子线程创建hander的区别。
之前的,在thread创建handler,为了确保在getLooper时,Looper已经创建完毕,笔者采用了thread.sleep的方法,不得不说,这种方法纯粹只是取巧。
我们看handlerThread中的getLooper方法(此方法在上述例子中在主线程中运行),先取得同步锁,然后判断mLooper是否为空,如果为空则使线程等待,等到mLooper不为空时,通知此线程重新启动运行。
在run方法中,Looper.myLoop(),返回此线程中的Looper,赋值给mLooper,运行到此时,getLooper方法中的mLooper不为空,
于是notifyAll(),通知所有进行wait的线程重新进入就绪状态。此线程调度,不得不说完美的解决了,在主线程获取Looper时,此时在子线程中Looper或许还未创建的问题。
本文若有错误之处,麻烦指出。如果觉得有可取之处,麻烦点赞支持一下。 --箫洛