Android消息机制主要就是指Handler的运行机制;
借用网络的图片
图解:
1.以Handler的sendMessage方法为例,当发送一个消息后,会将此消息加入消息队列MessageQueue中。
2.Looper负责去遍历消息队列并且将队列中的消息分发给对应的Handler进行处理。
3.在Handler的handleMessage方法中处理该消息,这就完成了一个消息的发送和处理过程。
这里从图中可以看到参与消息处理有四个对象,它们分别是 Handler, Message, MessageQueue,Looper。
*ThreadLocal 定义
ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有再指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
ActivityThread类中mian方法中,注意看Looper.prepareMainLooper();的调用,会为当前这个App创建出全局唯一的Looper对象;
package android.app;
......
public final class ActivityThread extends ClientTransactionHandler {
......
......
public static void main(String[] args) {
......
......
//创建出App全局唯一的Looper对象
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
当ActivityThread类的main方法中调用了Looper.prepareMainLooper的时候,会先创建出当前主线程唯一的Looper对象,并调用ThreadLocal的set方法将这个Looper对象传了进去,在创建Looper对象的时候,构造方法中又创建了当前这个Looper中的唯一的消息队列MessageQueue和获取到当前线程保存到Looper中唯一的mThread线程对象中;
package android.os;
......
public final class Looper {
......
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
......
final MessageQueue mQueue;
final Thread mThread;
......
//构造方法
//在这个构造方法中,有创建了当前Looper唯一的消息队列MessageQueue和将当前线程复制到了全局的mThread线程对象中
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
......
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//这里为我们创建了全局唯一的Looper对象
sThreadLocal.set(new Looper(quitAllowed));
}
......
}
接着上面,创建主线程唯一的Looper对象之后调用了ThreadLocal类中的这个set方法,将Looper对象传了进来;
传入进来的时候,因为当前是主线程第一次创建Looper对象,并set进来,所以这类获取ThreadLocalMap的时候是个null,然后走到下面的else当中,去给当前线程Thread创建ThreadLocalMap并保存到threadLocals 这个全局变量中;
ThreadLocalMap是键值对的方式保存,key是当前线程,value是当前线程的Looper对象;
public class ThreadLocal<T> {
......
public void set(T value) {
//获取到当前线程
Thread t = Thread.currentThread();
//通过当前线程去获取ThreadLocalMap对象
//这里的ThreadLocalMap拿的是当前这个Thread线程中的变量ThreadLocalMap 对象 而一路跟进来,这里的线程肯定是主线程吧
//这里是第一次进来,所以当前这个主线程肯定是没有ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//所以当前线程会走入这个创建ThreadLocalMap的方法中,并且保存到当前这个线程中的threadLocals变量中
createMap(t, value);
}
......
void createMap(Thread t, T firstValue) {
//创建ThreadLocalMap并赋值保存到当前线程的threadLocals 对象中;
//这个时候全局唯一的主线程中的这个threadLocals 对象就有了吧
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
......
}
相信大家看完这里,就能明白ActivityThread类main方法中会给我们创建出全局唯一的主线程的Looper对象,和Looper对象中会给我们创建出当前主线程Looper对象全局唯一的消息队列以及赋值了当前线程,也可以说是当前主线程的消息队列;
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
我们跟进这个Handler类里面去看看
创建Handler的时候,通过Looper.myLooper去获取了全局唯一主线程的Looper对象;这里获取到的Looper对象是不为空的,为什么不为空,请往下翻,下面有解释;
然后将获取的主线程Looper的消息队列变量赋值到了当前这个Handler的消息队列变量上;赋值回调Callback;
也就是说当前这个Handler是有个消息队列的,这个消息队列就是主线程唯一的消息队列;
package android.os;
public class Handler {
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
......
//这类是获取当前线程(因为是主线程创建的Handler,所以这里是获取了全局唯一的主线程Looper对象)的Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//拿到Looper对象的消息队列,赋值到了当前Handler的消息队列变量上面;
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
}
Looper类中的myLooper方法
这方法中调用了ThreadLocal的get方法
......
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
......
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
ThreadLocal的get方法
可以看到这里是获取了一遍当前线程;
通过当前线程去获取了ThreadLocalMap 对象;
记得ActivityThread类的main方法调用吧?
主线程的ThreadLocalMap 对象是存在了吧?已经创建并赋值到了当前线程的threadLocals变量上面了吧
public T get() {
//获取当前线程,因为是在主线程中创建的Handler对象;
//所以这类是拿到主线程
Thread t = Thread.currentThread();
//通过主线程获取到主线程的ThreadLocalMap对象
//因为主线程的ThreadLocalMap对象已经在ActivityThread的main方法中创建了,所以这里不为空
ThreadLocalMap map = getMap(t);
if (map != null) {
//进入到这里
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
我们跟进Handler中的sendMessage方法中去
注意:sendMessage这个方法最终调用的是sendMessageDelayed这个延时的方法,但是默认传了个0秒延时;
然后调入到sendMessageAtTime这个方法中,将全局唯一的消息队列赋值到了局部的消息队列变量中,然后调用enqueueMessage这个方法;
enqueueMessage方法中:
1、将当前Handler本身保存到了传入进来的Message的target变量上面,这时候这个message就持有了当前这个Handler了;
2、最后调用的是消息队列的enqueueMessage方法,将持有当前Handler引用的消息队列,以及更新的时间传入到了这个方法中
接着往下看↓
package android.os;
public class Handler {
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//将当前Handler本身保存到了传入进来的Message的target变量上面,这时候这个message就持有了当前这个Handler了
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//最后调用的是消息队列的enqueueMessage方法,将持有当前Handler引用的消息队列,以及更新的时间传入到了这个方法中
return queue.enqueueMessage(msg, uptimeMillis);
}
}
我们来看看MessageQueue里面的enqueueMessage方法
进来一条消息消息赋值到全局的Message上;
这方法中会有唤醒操作,继续往后看;
Message中有个对象池的概念:Message类使用了一个链表来实现对象池,最大支持50条消息对象;
Message类中有一个常量:private static final int MAX_POOL_SIZE = 50; 这是对象池中支持的最大消息对象;
里面有一个全局静态的sPoolSize指针,这个对象永远都是指向的是当前的第一个;
public final class MessageQueue {
......
Message mMessages;
......
boolean enqueueMessage(Message msg, long when) {
......
......
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
//将持有Handler引用的消息Message赋值到了全局变量中
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//这个死循环是不断的从对象池中去拿下一条数据
for (;;) {
//将当前消息赋值到上一条消息
prev = p;
//获取下一条消息赋值到当前
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
}
我们回到App启动的ActivityThread 类中的main方法中
Looper.loop(): 这类调用了Looper.loop()方法,这个方法是开启当前全局唯一的主线程Looper对象的消息循环;
往下看,我们跟进这个Looper.loop方法中去,往下看↓
package android.app;
......
public final class ActivityThread extends ClientTransactionHandler {
......
......
public static void main(String[] args) {
......
......
//创建出App全局唯一的Looper对象
Looper.prepareMainLooper();
......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
Looper类中的loop方法
这类我们只看主要的调用了,省略掉其他的一些判断和啥的…
1、myLooper方法我们上面已经解释了吧?就是获取当前的Looper对象;
2、拿到这个Looper对象中的消息队列;
3、无限循环,去消息队列中取消息,然后调用msg.target.dispatchMessage(msg);去分发消息;
是否还记得target这个值???
这个是当前这个消息Message对象中存放的Handler引用;
这里就是说,最后通过调用当前handler吧消息通过dispatchMessage方法分发出去了。
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对象中的消息队列
final MessageQueue queue = me.mQueue;
//开启无限循环
for (;;) {
//从消息队列中取出下一条消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
......
//调用msg对象中的handler的dispatchMessage方法把消息分发出去;
msg.target.dispatchMessage(msg);
}
}
来看看这个Handler的dispatchMessage方法
Loop方法中,无限循环,把消息传入到这个方法中;
判断了下消息中是否有回调,如果没有,调用handleCallback方法;
如果有回调,在判断了下当前Handler中是否有回调接口,如果有的话,直接用Handler的回调接口,把消息分发出去,如果没有的话,调用handleMessage方法,把消息分发出去;
还记得handleMessage这个方法不?我们在创建Handler的时候,会去重写的handleMessage方法,用来处理消息的;
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//创建handler 重写的handleMessage方法
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
public void handleMessage(@NonNull Message msg) {
}
Looper 的阻塞主要是靠 MessageQueue 来实现的,在next()@MessageQuese 进行阻塞,在 enqueueMessage()@MessageQueue 进行唤醒。主要依赖 native 层的 Looper 依靠 epoll 机制进行的。
来看看这个消息队列MessageQueue的next方法
nativePollOnce(ptr, nextPollTimeoutMillis) 这是调入到这个方法,注意看这类是一个native层的方法…阻塞在这个方法上面,等待唤醒;
native层中也是一个死循环;
被唤醒之后,才会接着下面的代码继续往下走;
阻塞和延时,主要是next()中nativePollOnce(ptr, nextPollTimeoutMillis)调用naive方法操作管道,由nextPollTimeoutMillis决定是否需要阻塞nextPollTimeoutMillis为0的时候表示不阻塞,为-1的时候表示一直阻塞直到被唤醒,其他时间表示延时。
那为什么主线程这类阻塞了,而没造成app的卡死ANR?
有人的解释是:native层会帮我们做一些资源的释放;
public final class MessageQueue {
......
private native void nativePollOnce(long ptr, int timeoutMillis);
......
Message next() {
......
......
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//消息的阻塞 nextPollTimeoutMillis时间
nativePollOnce(ptr, nextPollTimeoutMillis);
......
......
......
}
}
唤醒也同样是在消息队列MessageQueue中,这是在enqueueMessage方法
同样是调用的native蹭的nativeWake方法做唤醒操作
private native static void nativeWake(long ptr);
boolean enqueueMessage(Message msg, long when) {
......
......
synchronized (this) {
......
......
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
对Handler中的Native层调用需要了解的请移步
转载-Handler消息机制(native层)
mThread是UI线程,这里会检查当前线程是不是UI线程。那么为什么onCreate里面没有进行这个检查呢。这个问题原因出现在Activity的生命周期中,在onCreate方法中,UI处于创建过程,对用户来说界面还不可视,直到onStart方法后界面可视了,再到onResume方法后界面可以交互。从某种程度来讲,在onCreate方法中不能算是更新UI,只能说是配置UI,或者是设置UI的属性。这个时候不会调用到ViewRootImpl.checkThread(),因为ViewRootImpl没被创建。而在onResume方法后,ViewRootImpl才被创建。这个时候去交互界面才算是更新UI。
setContentView只是建立了View树,并没有进行渲染工作(其实真正的渲染工作是在
onResume之后)。也正是建立了View树,因此我们可以通过findViewById()来获取到View对象,但是由于并没有进行渲染视图的工作,也就是没有执行ViewRootImpl.performTransversal。同样View中也不会执行onMeasure(),如果在onResume()方法里直接获取View.getHeight()/View.getWidth()得到的结果总是0。
Handler的handleMessage方法中(或者run方法)处理消息,如果这个时候一个来了一条延时消息,会一直保存在主线程的消息队列里面,并且会影响系统对Activity的回收,造成内存泄露
感谢各位童鞋观看到最后,一起共勉加油,大神勿喷,谢谢~~