Handler源码分析记录

294 阅读2分钟

Handler机制源码分析

1.Message

//Message:数据载体
public int what;    //标识(id)
public int arg1;    //保存int数据
public int arg2;    //保存int数据
publib Object obj;  //保存任意数据
long when;          //记录应该被处理的时间值
Handler target;     //用来处理消息的Handler对象,就是发送消息的handler
Runnable callback;  //用来处理消息的回调器(一般不用)
Message next;       //指向下一个message用来形成一个链表
private static Message sPool; //用来缓存处理过的Message,以便复用

Message obtain()    //利用了Message中的消息池(sPool)
    if (sPool != null) {
        Message m = sPool;
        sPool = m.next;
        m.next = null;
        m.flags = 0; // clear in-use flag
        sPoolSize--;
        return m;
    }

2.Handler

//Handler:发送消息、处理消息、移除消息

//发送消息
sendMessage(@NonNull Message msg)
    sendMessageDelayed(msg, 0);
    
sendEmptyMessage(int what)
    sendEmptyMessageDelayed(what, 0);
    
sendEmptyMessageDelayed(int what, long delayMillis)
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
    
sendMessageDelayed(@NonNull Message msg, long delayMillis)     
    if (delayMillis < 0) { //容错判断
        delayMillis = 0;
    }
    sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)

sendMessageAtTime(@NonNull Message msg, long uptimeMillis)
    enqueueMessage(queue, msg, 0); //将消息添加到消息队列
    
enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis)
    msg.target = this;  //保存发送消息的Handler对象 
    queue.enqueueMessage(msg, uptimeMillis); //调用消息队列保存消息对象

//移除消息
removeMessages(int what) //移除消息
    mQueue.removeMessages(this, what, null);  //调用消息队列,移除它内部的指定what消息

//处理消息
handleMessage(@NonNull Message msg)  //处理消息的回调方法

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) { //如果消息可以自己处理,让消息自己处理
        handleCallback(msg);
    } else {
        if (mCallback != null) { //如果Handler对象中有回调监听器,调用回调器来处理
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg); //让Handler的handlerMessage()来处理
    }
}

3.MessageQueue

//MessageQueue 存储消息的以Message的when排序优先级队列
enqueueMessage(Message msg, long when) //将Message添加到队列中
    msg.when = when; //指定消息应该被处理的时间
    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; //最终结果:消息队列按when来排序的
    
    nativeWake(mPtr); //通过本地方法实现对等待状态的底层线程        
    
Message next()  //取出一个合适的Message对象(可能不会立即返回)
    nativePollOnce(ptr, nextPollTimeoutMillis); //本地方法,会导致可能处理等待状态,但不会阻塞线程
    Message msg = mMessages; //取出消息队列中的第一个消息
    return msg;

4.Looper

//Looper:从MessageQueue中获取当前需要处理的消息,并交给Handler处理
loop() //核心方法
    final Looper me = myLooper(); //得到Looper对象
    final MessageQueue queue = me.mQueue; //得到消息队列对象
    for (;;) { //无限循环
        Message msg = queue.next(); //从消息队列中取出消息(可能阻塞)
        msg.target.dispatchMessage(msg); //调用Handler去分发并处理消息
    
        msg.recycleUnchecked();//回收利用Message
    }    

Handler相关的一些问题

Handler 引起的内存泄露原因以及最佳解决方案

  • 主线程的Looper对象的生命周期 = 该应用程序的生命周期
  • 在Java中,非静态内部类 & 匿名内部类都默认持有外部类的引用

Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。 这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。

解决:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并在Acitivity的onDestroy()中调用handler.removeCallbacksAndMessages(null)及时移除所有消息。

为什么我们能在主线程直接使用 Handler,而不需要创建 Looper ?

通常我们认为 ActivityThread 就是主线程。事实上它并不是一个线程,而是主线程操作的管理者。在 ActivityThread.main() 方法中调用了 Looper.prepareMainLooper() 方法创建了 主线程的 Looper ,并且调用了 loop() 方法,所以我们就可以直接使用 Handler 了。

Handler里的 Callback 能干什么?

Handler.Callback 有优先处理消息的权利 ,当一条消息被 Callback 处理并拦截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不会被调用了;如果 Callback 处理了消息,但是并没有拦截,那么就意味着一个消息可以同时被 Callback 以及 Handler 处理。