Android消息机制主要是指Handler的运行机制。在开始之前先问下,考虑以下几个问题:
1 : 什么是Message?
通俗讲就是,两个线程之间通信的传递的介质就是Message。
2:Message的成员变量:
public int what; //用于标记传递执行什么操作,通常用于switch判断。
public int arg1; //如果传递的数据是整数类型就可以使用arg1或者arg2,不用再创建bundle
public int arg2;
public Object obj; //在进程间通信时,传递数据,通常使用setData传递数据,需要实现Android方式的序列化
public Messenger replyTo; //在进程间通信时,可以通过replyTo给服务端传递一个客户端的Messenger的对象,此时服务端就可以使用Messenger发送消息给客户端
public int sendingUid = -1;
/*package*/ int flags; //标记位,标记Message是否正在被使用。
/*package*/ long when; //消息的什么是有被执行。
/*package*/ Bundle data; //传递数据,封装成Bundle对象
/*package*/ Handler target; //新建此消息的handler对象
/*package*/ Runnable callback; // handler.postMessage的runnable对象
/*package*/ Message next;
private static final Object sPoolSync = new Object();
private static Message sPool; //回收消息池
private static int sPoolSize = 0; //回收消息池此时的长度
private static final int MAX_POOL_SIZE = 50; //回收消息池的最大长度
private static boolean gCheckRecycle = true; //消息是否能够被回收。
从上可以看出,如果不考虑进程间通信,我们关心的就是 when, target,flags 以及消息回收池。flags很重要,这是个标记位,用于标记Message此刻的状态,如果消息处于0状态,表明Message可以被使用,但是消息处于FLAG_IN_USE状态,也就是说消息此刻正在被使用或者二次入队列,此此时将抛出异常:“This message is already in use.”
其实sPool本质是一个单向链表,内部存储着回收的消息,那么问题来了为什么采用链表存储回收的消息呢?这个问题看了消息的创建源码会找到答案。
3:Message的创建
对于很多开发者都是通过new Message()新建消息的。但是官方建议我们通过以下两种方法创建消息:
1 : Message.obtain();
2 : Handler.obtainMessage();
再看下obtain()和obtainMessage的源码:
public static Message obtain() {
synchronized (sPoolSync) { //加锁
if (sPool != null) { //消息回收池不是null
Message m = sPool; //获取链表头节点
sPool = m.next; //获取第一个Message
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--; //消息回收池长度-1
return m; //返回此消息
}
}
return new Message(); //如果是空链表,就新建Message。
}
从上述代码可以看出,obtain先判断回收池是否是null,如果是null,就new Message,如果不是null,就从回收池获取一个旧的Message,然后将消息的flags置为0,返回消息。整体思想就是重复利用创建过的Message,这也就是为什么官方建议我们这样做,肯定是为了节约资源啊。
接下来我们再看下Handler的obtainMessgae()源码:
public final Message obtainMessage()
{
return Message.obtain(this);
}
从上述代码可以看出,this指的就是Handler的引用,也就是Message的target变量。最终调用的还是Message内部的obtain(Handler target)。源码如下:
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
上述两种方法的唯一区别就是,是否是初始化了Message中的target变量。
4:Message的回收
既然有创建对象,那么同理也就有回收对象,Message的回收源码如下:
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.
//从上述的英文文档可以看出,刚方法就是将Message回复原始状态,也就是说以前是怎么样的,现在就是怎么样的,
//但是唯一不同时flags对象置为FLAG_IN_USE对象,不能重复入队。
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就是介质,相当于我们从一个Activity到另一个Activity要传递的数据,只不过这类型是固定的:Message。
注意点:
1:什么是MessageQueue?
MessageQueue中文翻译过来就是:消息队列。通常理解是:MessageQueue是存储消息的,是一个队列。但是MessageQueue是怎样工作的呢?MessageQueue真的是一个队列吗?
2:MessageQueue工作原理
我们先看下MessageQueue的创建,MessageQueue的构造方法如下:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed; //队列是否能够退出
mPtr = nativeInit(); //底层使用的Code
}
那么MessageQueue在哪里实例化呢?是在Looper的构造方法里:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到,这个是在Looper实例化的,通过上述代码可以了解到,创建一个Looper就会创建一个MessgaeQueue。
接下来看下MessageQueue的成员变量:
// True if the message queue can be quit.
private final boolean mQuitAllowed; //是否能够退出
@SuppressWarnings("unused")
private long mPtr; // used by native code
Message mMessages; //链表的头节点(思考:队列里怎么会有链表呢?)
private final ArrayList mIdleHandlers = new ArrayList();
private SparseArray mFileDescriptorRecords;
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuitting;
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
private boolean mBlocked; //是否阻塞?
// The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
private int mNextBarrierToken;
通过上述代码,可以思考一个问题:成员变量里怎么会有链表Message?
3:Message入队
既然是队列,就肯定有消息入队嘛,代码如下:
//思考:我怎么知道这个是消息入队呢?
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) { //判断消息是否持有发送消息的handler对象
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) { //链表是null
// 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;
}
从上述代码可以看书,消息入队之前会判断三个条件:
判断完了之后,就是消息入队了,但是,这代码好像是链表的插入操作啊?别好像,本来就是,所以我们可以基本断定MessageQueue实际上操作的是消息链表,上一个问题:成员变量里怎么会有链表Message?是不是有答案了呢?
那么又有个问题了,这个方法什么时候调用呢?
4:Message出队
既然消息入队了,那么得有出队啊,源码如下:
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
//从上述翻译来看:如果消息循环已经退出并被释放,则返回这里。
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) { //死循环
//如果有消息过段时间再处理,就执行如下方法。Binder管道连接,等待到手通知消费消息
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) { //加锁
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//遍历消息链表,从链表中取出一个msg。
if (msg != null && msg.target == null) { //判断消息是否为null并且是否拥有发送消息的handler
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) { //消息不为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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else { //没有消息可以执行了
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
// 现在处理退出消息,处理所有挂起的消息。
//消息队列是否需要退出,处理所有的消息,或消费或回收
if (mQuitting) {
dispose(); //调用的底层C代码,处理还未处理的消息,准备退出队列了,此方法是安全的退出方式。默认是安全退出方式
return null; //返回null, Looper退出的判断条件
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
//接下的就是处理将来要执行的消息了。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
从上述代码可以看出出队的基本步骤:
通过入队和出队,可以总结出,其实MessageQueue本质就是:内部维护了一个消息链表,操作都是链表的插入和删除操作。
思考:入队方法什么时候调用呢?
5:MessageQueue退出
关于MessageQueue的退出方法代码如下:
void quit(boolean safe) {
if (!mQuitAllowed) { //队列是否允许退出,这个值是在MessageQueue构造的时候传入的。
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) { //队列已经退出
return;
}
mQuitting = true;
if (safe) { //是否是安全的退出
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
可以看出就是遍历链表,然后回收所有的消息。再看下安全的退出removeAllFutureMessagesLocked()方法源码:
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) { //如果链表头节点是延迟消息,那么直接回收掉所有的消息
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) { //如果消息链表是null,返回
return;
}
if (n.when > now) { //找出链表中的第一个延迟消息
break;
}
p = n;
}
p.next = null;
do { //循环删除第一个延迟消息的所有的消息
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
结论:
不安全的退出:删除所有的消息
安全的退出:删除第一个延迟消息之后的所有消息(包括第一个延迟消息)。
对于这个结论,至少源码是这样告诉我们的,但是还有没有其他的区别呢?看完Looper应该会有惊喜~
思考:退出什么时候调用呢?
刚开始接触Android的时候,就知道一点,looper中有个Loop方法,能够从MessageQueue中去消息。那么具体怎么去消息呢?
1:Looper的成员变量
static final ThreadLocal sThreadLocal = new ThreadLocal(); //存储线程不同线程的不同数据
private static Looper sMainLooper; //Ui 主线程的 sMainLooper
final MessageQueue mQueue; //关联的消息队列
final Thread mThread; //该Looper所在的线程
private Printer mLogging;
private long mTraceTag;
/* If set, the looper will show a warning log if a message dispatch takes longer than time. */
private long mSlowDispatchThresholdMs;
从成员变量可以看出,Looper内关联的有MessageQueue对象,创建了当前线程对象,自带Ui主线程的Looper。这三个都好说,那么sThreadLocal又是什么呢?从它的泛型可以看出是Looper的ThreadLocal类,使用场景:以线程为作用域并且不同线程有不同的数据副本。
2:创建Looper对象
通常的创建Looper的方法是:
public static void prepare() {
prepare(true); //是否能够退出队列
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { //是否已经存储了Looper对象,也就是说该线程下,是否已经创建过Looper了,
//如果Looper已经存在了,就异常显示
throw new RuntimeException("Only one Looper may be created per thread");
}
//新建Looper对象,并且将Looper添加到ThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
//新建MessageQueue
mQueue = new MessageQueue(quitAllowed);
//新建Looper所在的线程对象
mThread = Thread.currentThread();
}
从上述代码可以看出,创建Looper做了三件事:
其中从sThreadLocal.get()的判null操作,我们可以得出结论:一个线程只能存在一个Looper!
等等,这个MessageQueue的参数也就是quitAllowed嘛,也就是是否允许退出队列。
3:Looper的loop()方法
在开始loop()方法之前,有必要先看个方法myLooper():
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
//从翻译来看:获取与当前线程相关联的Looper对象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
从上述方法可以猜测ThreadLocal存储的是:Looper以及Looper关联的线程
接下来是重头戏loop()方法:
public static void loop() {
final Looper me = myLooper(); //返回当前线程关联的Looper对象
if (me == null) { //如果Looper为null,表明当前线程不存在Looper,也就是没有实例化Looper对象
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //获取当前线程中Looper对象对应的MessageQueue对象
// 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(); // 从MessageQueue中取出消息。有消息就取出,没消息就阻塞
if (msg == null) { //消息是否为null,这个应该在MessageQueue的next方法中,消息队列退出之后会返回null
// No message indicates that the message queue is quitting.
return;
}
...
msg.target.dispatchMessage(msg); //回调给发送消息的handler处理
...
msg.recycleUnchecked(); //处理完毕,消息回收
}
}
从上出代码可以看出,loop方法内部实际上是个死循环,调用MessageQueue的next()方法获取消息,等等,那之前的问题(队列中的next()方法在哪里调用)是不是解决了?有消息就处理,没消息阻塞。
那么怎么消费这个消息了呢? msg.target.dispatchMessage(msg);直接给出了答案,回调给了发送消息的target对象,让handler处理,从这里可以看你出,其实loop并不是真正的消费消息,只是起到一个消息调度的作用,真正消费消息的还是发送该消息的handler。
等等,loop死循环,那么怎么跳出死循环呢? 从源码来看只有满足一个条件,那就是返回的消息为null, 这样就退出死循环了。从MessageQueue的next()方法中可以看到,
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
也就说mQuitting为true的时候返回null, 那么怎么mQuitting怎么为true呢?从上文中的MessageQueue的quit代码中可以找到给mQuitting赋值的踪迹:mQuitting=true; 那这个MessageQueue的quit(boolean safe)什么时候调用呢?
4:Looper的退出
对于Looper的退出方式,Google给出了两种方式:
/**
* Quits the looper.
*
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.
*
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
*
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
*
*
* @see #quitSafely
*/
//注释最后一句的翻译是:考虑使用{@ Link quitSafely},以确保所有未决工作都有秩序地完成。
public void quit() {
mQueue.quit(false); //不安全退出
}
/**
* Quits the looper safely.
*
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
*
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
*
*/
//中间几句话翻译一下:在已交付的消息队列中已被处理。然而,延迟的消息与未来的到期时间将不会在循环结束之前处理
//换句话说:在退出之前,可以立即处理的消息,可以就都发送给handler处理,但是延迟的消息将不会得到处理。
public void quitSafely() {
mQueue.quit(true); //安全退出
}
从上面的源码可以看出,MessageQueue的quit(safe)方法是在Looper中调用的。从上面的翻译我们可以重新总结上面的安全和不安全的退出区别:
至此,looper源码就分析完了,总结一下Looper和MessageQueue的关系,Looper中loop方法调用MessageQueue的next方法获取消息,Looper中调用MessageQueue的quit(safe)方法退出loop方法。从ThreadLoca可以看出,一个线程只能有一个Looper,一个Looper对应一个MessageQueue,这些信息都是存储在ThreadLocal中。
现在消息出队有了,那么消息入队,以及消息消费在哪里呢? 不用想,铁三角关系,就剩handler了。
handler是干什么的?
往大了说:线程切换
往小了说:想MessageQueue添加消息和接收Looper中回调的消息
1:handler的成员变量
final Looper mLooper; //当前线程的looper
final MessageQueue mQueue; //消息队列
final Callback mCallback; //回调,构造函数的参数
final boolean mAsynchronous; //消息是否是异步的
IMessenger mMessenger; //跨进程间通信时使用
从成员变量可以看出:handler持有Looper对象和MessageQueue。
2:handler的创建
对于Handler的创建,有三种方法:
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();//获取一个Looper对象
if (mLooper == null) { //判断当前线程的Looper对象是否为null
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; //获取MessageQueue
mCallback = callback; //callback回调
mAsynchronous = async; //是否是异步
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
通常我们使用的就是两种:
从上述代码可以看到mQueue和mLooper都是在创建new Handler的时候直接获取到的,其中:mLooper = Looper.myLooper() 代码品味一下,myLooper()方法不就是获取一个Looper对象嘛,但是日常的问题:是先有handler还是先有Looper?
从源码的Looper的判空操作,就可以谁知道:先创建Looper,在创建handler,这才是正确的操作姿势,不然就报错。
注意:开发者在使用第一种创建方法的时候,要注意内存泄漏问题;
3:handler发送消息(入队)
发送消息总结一下就两种:sendXXX 和 postXXX
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
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);
}
从上述代码可以看出:其实这两种方法最终调用的方法是一样的,都是sendMessageDelayed;只不过是传递的参数不同,第一种方法处理消息有两种:参数实现callback的接口的类或者复写handlerMessage()方法;第二种方法处理消息:在Runnable的run()方法中处理消息。再看下getPostMessage方法,可以看出该方法将Runnable的一整个对象包装成了一个Message对象返回。最终调用的是MessageQueue的enqueueMessage方法,流入示下(以postXXX为例):
postXXX —> send —> enqueueMessage
至此消息入队的入口找到了。
4:handler接收消息(消费消息)
对于消费消息,在Looper的loop方法中我们做出发现了,将消息回调给handler的踪迹:msg.target.dispatchMessage(msg);,那么看下dispatchMessage(msg)源码:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) { //实现的Runnabel接口是否为null,如果不是null,就执行handleCallback方法
handleCallback(msg);
} else {
//这种情况就是new Handler(Callback)
if (mCallback != null) { //判断Callback接口是否为null, 如果不是null,就执行如下操作。
if (mCallback.handleMessage(msg)) { //Callback接口内的handlerMessage方法是否返回true,如果返回true,后面的代码就不执行。
return;
}
}
//以上判断都不成立(空的构造方法,复写handlerMessage(msg)方法)
handleMessage(msg);
}
}
从以上的代码可以总结出:
其实归根结底,消费消息的还是在handler的回调中,然后交给开发者自己处理。
至此handler,looper,messagequeue的源码基本就分析完了,总结一下handler的工作流程:
等等,还有一个知识点就是Looper中创建的ThreadLocal,在Looper中调用了ThreadLocal的set方法,参数是looper,实际上操作的是ThreadLocalMap对象的set或者get方法,换句话说:TheadLocalMap才是真正的以线程为作用于存储不同的数据。ThreadLocalMap内部的Entry对象的key也就是ThreadLocal,value就是创建的Looper对象。其中key是弱引用,value是强引用。强引用? 是不是嗅到了“内存泄漏”的味道。没错,乱用ThreadLocal会导致内存泄漏的。
那么问题来了:是怎么导致内存泄露呢?
因为Key是弱引用,那么在GC的时候,是会回收ThreadLocal对象的,那么就导致了Key为null,此时强引用的value也就是存储的数值就无法释放,如果该线程一直存在,那么就一直无法释放,这就导致了内存泄漏。
那么有的同学会说:为什么key使用弱引用呢?
1:key强引用:此时如果我们想回收ThreadLocal,但是ThreadLocalMap中含有ThreadLocal对象,就导致ThreadLocal无法被回收,直接导致ThreadLocal内存泄露(value泄漏一直都是可能存在的)。
2:key弱引用:此时如果我们想回收ThreadLocal,由于是弱引用,所以可以直接回收,那么就只有value可能存在泄漏问题了。
从上述两个点来看在value都可能存在内存泄漏的前提下,key肯定设置成弱引用为好,这样最起码不会导致ThreadLocal泄漏。那么怎么解决key为null,value不为null的泄漏问题呢?
ThreadLocal内部已经替我们想好了,在ThreadLocal的set,get, remove方法内,都会删除key为null的value,但是毕竟是属于开发者自己手动调用的,所以最佳的实现方案就是:在不使用ThreadLocal的时候,都需要调用remove方法。
深入分析 ThreadLocal 内存泄漏问题
本文感谢《Android开发艺术探索》