1. Handler使用示例
public class DownloadActivity extends AppCompatActivity implements View.OnClickListener {
private TextView downloadTV;
private Button downloadBtn;
private Handler handler = new Handler() {//主线程创建Handler并复写handleMessage
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
downloadTV.setText("下载完成");
}
};
public void onClick(View v) {
switch (v.getId()){
case R.id.download_btn:
new Thread(new Runnable() { //子线程使用handler执行sendEmptyMessage发送Message
@Override //消息为空,可以使用Message.what指定消息类型, Message.obj指定消息主体
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(1);
}
}).start();
break;
}
}
}
Handler用法总结:
首先定义一个继承自Handler的MyHandler并复写handleMessage方法,主线程实例化MyHandler,子线程调用Handler.sendMessage发送消息,回调主线程的Handler.handleMessage
2. 主线程创建Looper的过程
每个定义了Handler 的线程都必须有一个 Looper ,主线程当然也不例外,上面的示例中Looper如何创建?
通常认为ActivityThread就是Activity的主线程
分析:Looper.prepareMainLooper();
分析:Looper.loop();
主线程的Looper创建过程总结:
首先执行Looper.prepare()创建Looper,并创建MessageQueue保存到Looper.mQueue;
然后执行Looper.loop()进入死循环处理消息,取出MessageQueue中的Message,其中msg.target即为Message的发送者Handler,最终调用dispatchMessage回调handleMessage
3. 主线程创建Handler的过程
主线程创建Handler时,在Handler的构造函数中会取出主线程创建的Looper,并将Looper. mQueue保存到Handler. mQueue,由于主线程只有一个Looper,因此无论主线程创建多少个Handler,其中的messageQueue保存的都是Looper中的MessageQueue
4. 主线程创建Handler的过程
最终调用到:
取出Looper中的MessageQueue
将this即调用着Handler保存到message中的target中
Handler执行sendMessage时会调用enqueueMessage将消息的发送者Handler保存到Message.target,最后将Message放入调用者Handler中的MessageQueue的尾部,由于Handler中的MessageQueue即为Looper中的MessageQueue,所以Handler执行sendMessage时最终将Message保存到Looper中的MessageQueue中
备注:MessageQueue是单向链表形式存在的
5. Looper取出Message交给Handler处理
执行Looper.loop()会循环从MessageQueue取出Message,从Message.target找出Message的发送者Handler,回调Handler.handleMessage处理消息
6. MessageQueue为空会怎么样
a. MessageQueue为空时休眠
在执行Looper.loop()的过程中会执行Message msg = queue.next();当MessageQueue为空时会导致nextPollTimeoutMillis = -1;当下一次for循环执行nativePollOnce时,最后会执行nativePollOnce,通过jni调用pollOnce
当传入参数nextPollTimeoutMillis = -1,执行epoll_wait时会休眠可以看到这里使用到了Linux的epoll机制
epoll机制:
b. 下一次王MessageQueue放入Message时解除休眠
在Looper构造函数中执行:
可以看到当执行epoll_wait的时候会检测mWakeReadPipeFd和mWakeWritePipeFd两个文件句柄的值,当着两个句柄有数据时epoll会被唤醒
在下一次执行sendMessage时会执行nativeWake,当向pipe管道写端mWakeWritePipeFd写入唤醒信号1时,读端mWakeReadPipeFd会收到信号1,由epoll机制可知从MessageQueue取Message所在的线程会被唤醒
总结:
当MessageQueue为空时,执行MessageQueue.next取出下一个Message时会休眠,休眠采用epoll机制监听pipe管道读端mWakeReadPipeFd的数据,若无数据则休眠;当下一次执行sendMessage时会执行nativeWake,向pipe管道写端mWakeWritePipeFd写入唤醒信号,此时执行MessageQueue.next的线程被唤醒
7. 总结
a. Looper.prepare()方法
主线程创建Looper,并创建MessageQueue保存到Looper.mQueue
主线程创建多个Handler:Handler1、Handler2、HandlerN,每个Handler在创建的过程中都会将Looper.mQueue保存到自己的mQueue中
b. HandlerN.sendMessage()方法
将sendMessage的调用者HandlerN保存到msg.target中,并将msg保存到HandlerN.mQueue中,由于HandlerN.mQueue等于Looper.mQueue,故即将Message保存到Looper的MessageQueue中
c. Looper.loop()方法
死循环逐一取出保存在Looper的MessageQueue中msg,由于msg.target保存着msg的发送者HandlerN,调用msg.target. dispatchMessage,最终HandlerN.handleMessage
当MessageQueue为空时会使用epoll休眠,当下一个Message放入Message时会被唤醒