一篇比较好的博客:https://blog.csdn.net/Y_C_C/article/details/80605501
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(消息为空,可以使用Message.what指定消息类型,Message.obj指定消息主体)
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(1);
}
}).start();
break;
}
}
}
主线程创建Handler并复写handleMessage
子线程使用handler执行sendEmptyMessage发送Message(消息为空,可以使用Message.what指定消息类型,Message.obj指定消息主体)
由上可知创建实例化对象的两种方式:
方式1:
private Handler handler = new Handler() { //主线程创建Handler并复写handleMessage
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
downloadTV.setText("下载完成");
}
};
方式2:
private MyHandler handler = new MyHandler();
public class MyHandler extends Handler { //主线程创建Handler并复写handleMessage
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
downloadTV.setText("下载完成");
}
};
Handler用法总结:
首先定义一个继承自Handler的MyHandler并复写handleMessage方法,主线程实例化MyHandler,子线程调用Handler.sendMessage发送消息,回调主线程的Handler.handleMessage
2. Handler的源码分析 — 分析sendEmptyMessage如何回调handleMessage
2.1 Handler发送消息到Message的过程 — sendEmptyMessage
//Handler.java
Handler.sendMessage
sendMessageDelayed
sendMessageAtTime
enqueueMessage
msg.target = this; //1. 将Handler保存到Message.target
queue.enqueueMessage //queue为MessageQueue
//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
/*for循环找到MessageQueue最后一个Message*/
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false; //表明MessageQueue不为空
}
}
msg.next = p; //p为空
prev.next = msg; //prev为最后一个Message,2. 将msg放到MessageQueue的尾部
}
if (needWake) {
nativeWake(mPtr);
}
}
总结:执行sendMessage时会调用enqueueMessage将消息的发送者Handler保存到Message.target,最后将Message放入MessageQueue的尾部(MessageQueue是单向链表形式存在的)
2.2 Looper取出Message的过程 — handleMessage
好文:https://m.2cto.com/kf/201610/556770.html
Activity主线程对应:ActivityThread.main
public final class ActivityThread {
public static void main(String[] args) {
...
Looper.prepareMainLooper(); //第一步:执行Looper.prepare()创建Looper
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
Looper.loop(); //第二步:执行Looper.loop()进入死循环处理消息
}
}
分析:Looper.prepareMainLooper();
public final class Looper {
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
sMainLooper = myLooper(); //从ThreadLocal中取出创建的Looper
}
}
private static void prepare(boolean quitAllowed) {
sThreadLocal.set(new Looper(quitAllowed)); //创建一个Looper,保存在ThreadLocal中
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //创建MessageQueue保存到Looper.mQueue
mThread = Thread.currentThread();
}
}
分析:Looper.loop();
public final class Looper {
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); //取出MessageQueue中的Message,如果MessageQueue为空则会阻塞,直到下一次执行sendMessage放入一个Message到MessageQueue中执行nativeWake唤醒
msg.target.dispatchMessage(msg); //msg.target即为Message的发送者Handler
}
}
}
public class Handler {
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //回调Handler.handleMessage
}
}
}
总结:执行Looper.loop()会循环从MessageQueue取出Message,从Message.target找出Message的发送者Handler,回调Handler.handleMessage处理消息
3. 几个问题
3.1 MessageQueue为空会怎么样
在执行Looper.loop()的过程中会执行Message msg = queue.next();如果MessageQueue为空则会阻塞
public final class MessageQueue {
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
/*取出MessageQueue最后一个消息*/
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
/*如果MessageQueue不为空*/
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
}
/*如果MessageQueue为空*/
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
}
}
当MessageQueue为空时会导致nextPollTimeoutMillis = -1;当下一次for循环执行nativePollOnce时,最后会执行
frameworks\base\core\jni\android_os_MessageQueue.cpp:
void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) {
mInCallback = true;
mLooper->pollOnce(timeoutMillis);
mInCallback = false;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
system\core\libutils\Looper.cpp:
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
result = pollInner(timeoutMillis);
}
}
int Looper::pollInner(int timeoutMillis) {
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
}
可以看到这里使用到了Linux的epoll机制
epoll机制:
epoll的作用:用来监测某目录下多个文件是否被修改,若被修改则读取修改的内容
epoll的用法:
a. epoll_create //创建epoll,返回文件句柄fd,以后可以通过文件句柄操作文件
b. 若要检测多个文件,则需要对每个文件,执行epoll_ctl(…,EPOLL_CTL_ADD,…),表示监测此文件
c. epoll_wait //等待某个文件可用,当检测数据,有数据时epoll就会返回;当检测空间,有空间时epoll就会返回
d. epoll_ctl(…,EPOLL_CTL_DEL,…),表示不再想监测某文件
其中在Looper构造函数中执行:
int wakeFds[2];
int result = pipe(wakeFds);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
表示:MessageQueue为空时,会使用epoll机制休眠监测mWakeReadPipeFd的数据,当文件句柄mWakeReadPipeFd有数据时会被唤醒
在下一次执行sendMessage时会执行nativeWake
//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
...
if (needWake) {
nativeWake(mPtr);
}
}
nativeWake最终会JNI调用:
void NativeMessageQueue::wake() {
mLooper->wake();
}
system/core/libutils/Looper.cpp
void Looper::wake() {
nWrite = write(mWakeWritePipeFd, "W", 1); //发送唤醒的信号
}
当向pipe管道写端mWakeWritePipeFd写入唤醒信号1时,读端mWakeReadPipeFd会收到信号1,由epoll机制可知从MessageQueue取Message所在的线程会被唤醒
pipe管道原理:https://blog.csdn.net/centor/article/details/76736601
总结:
当MessageQueue为空时,执行MessageQueue.next取出下一个Message时会休眠,休眠采用epoll机制监听pipe管道读端mWakeReadPipeFd的数据,若无数据则休眠
当下一次执行sendMessage时会执行nativeWake,向pipe管道写端mWakeWritePipeFd写入唤醒信号,此时执行MessageQueue.next的线程被唤醒
3.2 在主线程中不需要执行Looper.prepare()创建Looper,不需要执行Looper.loop()让Looper开始工作,Looper什么时候创建的
Activity里面有一个成员变量ActivityThread,就是Activity的UI主线程
ActivityThread的main方法会执行Looper.prepareMainLooper()创建Looper,执行Looper.loop()让Looper循环工作起来
ActivityThread.main
Looper.prepareMainLooper();
sMainLooper = myLooper();
return sThreadLocal.get();
Looper.loop(); //Activity主线程执行的最后一行代码
3.3 为什么在主线程执行死循环Looper.loop()时不卡顿
参考:https://blog.csdn.net/u012045061/article/details/50631581
执行Looper.loop();后主线程结束,在生命周期内写的方法都是在这个死循环内的,所以不会卡死
3.4 子线程创建的Looper和主线程创建的Looper冲突吗
不冲突,每个线程对应一个Looper,每个Looper对应一个MessageQueue,每个MessageQueue有多个Message,每个Message最多指定一个Handler处理消息
Handler原理总结:
a. 一个线程对应一个Looper,一个Looper对应一个MessageQueue,一个MessageQueue有多个Message,每个Message最多指定一个Handler处理消息
b. 线程1执行Handler.sendMessage()方法,会将线程2创建的Handler保存到Message.target中,并将Message放入MessageQueue
//Looper.prepare()方法,会new Looper()并将new MessageQueue()保存到Looper.mQueue中
c. Looper.loop()方法,不断从MessageQueue中取出Message,交给Message.target(即Handler)的handleMessage处理,从而回调线程2创建的Handler复写的handleMessage方法
//若MessageQueue为空则会使用epoll机制休眠,直到下一个Message放入MessageQueue执行nativeWake()被唤醒