android 消息传递机制
1 源码分析
handler构造函数:
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
一般来说我们都是用Handler()来生成Handler实例的那么我们来看看这个构造函数做了些什么吧:
public Handler() {
this(null, false);
}
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;
}
mLooper = Looper.myLooper();是什么意思,我们来看看它是怎么实现的
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
线程本地存储区(==Thread Local Storage,简称为TLS==),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域,这样问题就来了,为什么不同线程之间彼此不能访问对方的TLS区域?我们来看看ThreadLocal的set方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
在这个方法内部我们看到,首先通过getMap(Thread t)方法获取一个和当前线程相关的ThreadLocalMap,然后将变量的值设置到这个ThreadLocalMap对象中,当然如果获取到的ThreadLocalMap对象为空,就通过createMap方法创建。这又有了一个疑问,这个set方法不会发生并发嘛?线程隔离的秘密,就在于ThreadLocalMap这个类。ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取(对比Map对象来理解),每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。==因为每个线程的变量都是自己特有的,完全不会有并发错误==(当时不懂这句,自己写还是比较直观):
static class A implements Runnable {
int i;
@Override
public void run() {
for(int j=0;j<=2;j++){
i+=j;
}
System.out.println(i);
}
}
还有一点就是,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就是你所设置的对象了。接着我们来看看get方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
从上面这代码块可以知道,get是从ThreadLocalMap.Entry拿出来的,也是通过set放进去的,那在哪进行set的呢:
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));
}
当sThreadLocal里面没有东西的时候才会执行set,否则会RuntimeException,到这里来总结下,也就是说使用handler,必须先执行Loop.prepare,问题来了:
- 仔细看看这个方法prepare的参数有什么意义?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
。。。
}
quitAllowed这个参数所经过的方法,明显,mQuitAllowed只有Ui线程会设为false,这也是prepare方法默认为true的原因吧
- 为什么我们直接使用Handler的时候不需要执行这个方法呢
回到最开始Handler()这个构造函数,一般来说我们都是在UI线程中直接使用这个构造函数,从前面的分析我们应该知道,要使用Handler这个消息传递,那就应该提前使用prepare方法,我们就来做个实验,在UI线程中使用这个方法,不出意料,闪退:
Caused by: java.lang.RuntimeException:
Only one Looper may be created per thread
at android.os.Looper.prepare(Looper.java:90)
at android.os.Looper.prepare(Looper.java:85)
也就是上面sThreadLocal.get() != null这个条件,那肯定是在哪里已经执行过了,答案就是在ActivityThread的main函数里面:Looper.prepareMainLooper();ActivityThread6103行:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
注意到prepare里面的参数了嘛,上面也分析了只有在mian
thread中才会为false。
实例化Handler后我们可以用很多方法来发送消息,但最终都是通过enqueueMessage方法来传递消息的:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
实际上是执行MessageQueue.enqueueMessage:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
mQuitting,MessageQueue默认为false,那么哪里赋为true的呢。通过查找可以找到只有在quit(boolean safe)方法中,而且找不到赋为fasle的地方,这不就说明了当执行了MessageQueue.quit这个方法,MessageQueue也会被回收掉的,接着看后面msg.recycle():
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
看上去有点晕啊,先来看看Message,这个类的构造函数有很多,要说的就两个:
public static Message obtain()
public Message()
尽量不用第二种构造方式,先来分析分析第一种:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
sPool哪里有赋值呢,通过查找,也就只有上面的recycleUnchecked方法进行赋值了,综合来看,Message对象好像是被循环利用了,根据它的next属性,就知道它是链表的数据结构,这里用到了享元模式,如果用的第二种构造方式,就得重新分配内存了,
接着分析MessageQueue.enqueueMessage,mMessages属性可以说是MessageQueue的头,最先处理的消息。
接着看这个判断语句(p == null || when == 0 || when < p.when):p是当前的message,when表示消息处理的时间,它的值是距离开机的时间+延迟的时间,when
public static void 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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
关键的代码就是msg.target.dispatchMessage(msg);target就是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);
}
}
public void handleMessage(Message msg) {
//空方法
}
这些就是整个消息传递机制了
2 参考:
上一篇: webpack中利用require.ensure()实现按需加载
下一篇: 使用JMS进行消息传递
推荐阅读
-
Android 消息分发使用EventBus的实例详解
-
android studio 使用adb 命令传递文件到android 设备的方法
-
Android 消息队列模型详解及实例
-
Android View的事件分发机制
-
Android贝塞尔曲线初步学习第二课 仿QQ未读消息气泡拖拽黏连效果
-
Android开发笔记之:消息循环与Looper的详解
-
深入理解Android组件间通信机制对面向对象特性的影响详解
-
Android Handler之消息循环的深入解析
-
Android新浪微博下拉刷新(最新消息显示在最上面)
-
Android中通过Notification&NotificationManager实现消息通知