由浅入深全面分析Handler机制原理之源码

前言

在Android中提供了一种异步回调机制Handler。在开发中,我们都会接触到线程间的通信,也就是在子线程处理一些事务操作,处理完成后,通过Handler发送消息通知主线程根据事务消息更新UI。在源码中也到处看到Handler的身影。有时候开发过程中往往只知道怎么去使用它,从而不很少去了解它的原理与特性,下面我们通过Handler的使用去探索Handler的原理与特性。

目录

  • Handler的简单使用
  • Handler机制的构成
  • Handler机制组成成员之间的关系
  • Handler机制原理

先一张图,对下面在分析的内容有一个整体了解:


Handler的简单使用

我们模拟线程间的消息通信,在子线程中发出一条信息,在UI线程接收这个条消息,并打印出来,当然在开发中,可能业务不一样,但都是从子线程发送消息,在主线程更新UI。

/**
  * Author: 安仔夏天很勤奋
 * Date: 2020/09/20
 * Desc: 
 */
public class HandlerActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
    }

    //此处理程序类应该是静态的,否则可能会发生泄漏.(匿名Handler)
    Handler mHandler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            //更新UI 
            Log.e("lu","what: "+msg.what+"    obj :"+msg.obj);
        }
    };

    public void sendThreadMessage(View view) {
        //在异步中,Handler发送消息,让UI线程处理
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message m = new Message();
                m.arg1=2;
                m.obj="我是子线程的obj";
                m.what=1;
                mHandler.sendMessage(m);
            }
        }).start();
    }
}

简单的使用Handler是不是很简单,但是这样创建一个Handler匿名内部类,会存在内存泄漏风险。

Handler为什么会有内存泄漏风险?

当我们创建一个Handler匿名内部类时,编辑器为我们标出了黄色,并且提示如下:

This Handler class should be static or leaks might occur (anonymous android.os.Handler) less... (Ctrl+F1)
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.
由于handler定义为内部类,可能会阻止GC。如果handler的Looper或MessageQueue 非主线程,那么没有问题。如果handler的Looper或MessageQueue 在主线程,那么需要按如下定义:定义handler为静态内部类,当你实例化handler的时候,传入一个外部类的弱引用,以便通过弱引用使用外部类的所有成员。

解决创建一个Handler匿名内部类存在内存泄漏风险的一个写法,代码如下:

/**
 * Author: 安仔夏天很勤奋
 * Date: 2020/09/20
 * Desc: 
 */
public class HandlerActivity extends AppCompatActivity {
    MyHandler myHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        myHandler = new MyHandler(this);
    }

    static class MyHandler extends Handler{
        //弱引用
        WeakReference<HandlerActivity> weakReference;
        public MyHandler(HandlerActivity activity){
            weakReference = new WeakReference<>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            HandlerActivity activity = weakReference.get();
            if (activity !=null && !activity.isFinishing()){
                //更新UI
                Log.e("lu","what: "+msg.what+"    obj :"+msg.obj);
                activity.refreshUI();
            }
        }
    }
    
    private void refreshUI(){ }

    public void sendThreadMessage(View view) {
        //在异步中,Handler发送消息,让UI线程处理
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 发送一个消息,延时2分钟处理消息,此时handler是持有activity引用的
                myHandler.sendEmptyMessageDelayed(1,120_1000);
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(myHandler!=null){
            myHandler.removeCallbacksAndMessages(null);
        }
    }
}
  • Handler导致activity内存泄露的原因

    当Handler发送的一条消息,如果这条消息是延迟很久的,那么还在当退出组件(如Activity.finish())时,Handler发送的消息依然队列中,那么消息队列的消息到时间了,就会由Handler进行分发处理,若此时Handler声明为内部类(非静态内部类),我们知道内部类天然持有外部类的实例引用,当GC垃圾回收机制被调用时,那么就会导致组件activity无法回收,从而导致activity泄露。(关于GC回收机制的讨论就不展开讨论了。)

  • 使用静态内部类的作用

    因为静态内部类不持有外部类的引用,所以使用静态的Handler不会导致组件activity的泄露。Java的引用有四大引用:强引用,弱引用,软引用,虚引用(关于Java的引用作用就不展开讨论了)。

  • 使用static修饰同时,为什么还要用WeakReference 包裹外部类的对象?

    因为我们需要使用外部类的成员,由于不持有外部类的引用,怎么办呢,只能传入外部类引用(强引用),如通过"activity. "获取变量方法等,如果直接使用强引用,显然会导致activity泄露。使用弱引用,当组件activity销毁时,GC垃圾回收机制被调用时,根据GC Root可达性分析,那么弱引用的对象就会被回收。

  • 退出组件件时,清空。

    退出组件时,要及时调用Handler.removeCallbacksAndMessages(null)清空。

Handler机制的构成

Handler类

Handler机制有那些相关的类,我们从Handler的构造函数与mHandler.sendMessage(Message msg)函数作为入口查看源码。首先看看Handler类的构造函数源码,由于Handler重载了多个构造函数,我们就取Handler()作为切入点:

public Handler() {
    this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
    //...省略部分代码
    mLooper = Looper.myLooper();//获取Looper
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
            + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;//获取消息队列MessageQueue
    mCallback = callback;//初始化回调
    mAsynchronous = async;//是否为异步
}

当我们直接new Handler()时,内部直接调用this(null, false),其实Handler重载了多个构造函数,最终还是Handler(Callback callback, boolean async)构造函数,当然我们还可以调用Handler(Looper looper, Callback callback, boolean async)等构造函数 。从Handler构造函数中看出,Handler的成员变量mLooper、mQueue、mCallback、mAsynchronous都是赋值操作,所以Handler持有着一个Looper对象、MessageQueue对象、Callback回调。

Looper初始化时机

Looper.prepare()函数

在上述使用Handler时的Handler构造函数中看出, mLooper并不是直接new出一个Looper对象,而是在Handler构造函数中直接获取Looper对象(mLooper = Looper.myLooper()),那么Looper对象在那里创建了Looper对象呢。查看Looper源码:

public final class Looper {
    //quitAllowed  MessageQueue是否允许退出
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//创建MessageQueue对象
        mThread = Thread.currentThread();//Looper 绑定当前线程
    }
    public static void prepare() {
        prepare(true);
    }
    // quitAllowed  MessageQueue是否允许退出
    private static void prepare(boolean quitAllowed) {
        //如果不为null,说明当前线程已经存在对应的Looper对象,报出异常,保证一个线程只有一个Looper对象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//创建一个Looper对象并设置给ThreadLocal
    }
//获取当前线程的Looper对象
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

}

从源码我们可以看出,Looper是一个私有的构造函数,则不能在其他地方new出一个Looper对象,目的是为了确保一个Handler对应一个Looper。但Looper类中提供了两个prepare重载函数,最终还是调用prepare(boolean quitAllowed)函数,所以创建普通线程(子线程)创建Looper对象调用prepare()即可。prepare(quitAllowed)函数使用ThreadLocal 保存当前Looper对象。关于ThreadLocal的作用,先放一旁,下面再讨论分析。

主线程Looper创建的时机

基于上述使用例子,主线程的Looper什么时候创建的呢,当我们点击桌面图标启动APP时,会启动一个AMS(ActivityManagerService)如果还没有创建APP进程,那么就是Zygote出一个进程,创建一个进程对应的JVM,然后调用ActivityThread.main()函数启动Activity(这里涉及到APP的启动流程就不展开了,感兴趣的可以自行查阅学习)。在一个APP中只有一个主线程,那么主线程的Looper也只有一个,而ActivityThread就是我们常说的主线程或UI线程,ActivityThread的main函数是整个APP的入口,所以主线程的Looper的创建时机就在ActivityThread的main函数里。看成一下源码:

public static void main(String[] args) {
    //...省略前面的代码
    Looper.prepareMainLooper();//主线程的Looper初始化
    //...省略部分代码
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);//建立Binder通道 (创建新线程) 
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();//初始化主线程的Handler
    }
    //...省略部分代码
    Looper.loop();//开始轮询
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

public static void prepareMainLooper() {
    prepare(false);//初始化主线程的Looper
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper(); //主线程Looper初始化完成后赋值给sMainLooper
    }
}

从上述的源代码中可以看出,创建主线程Looper轮询器创建的过程主路线为:ActivityThread.main() -- > Looper.prepareMainLooper() --> Looper.prepare(false) --> sThreadLocal.set(new Looper(quitAllowed)) --> thread.getHandler() 初始化主线程Handler--> Looper.loop()

用一张图来展示,更加易懂:


在主线程或子线程初始化Looper,需要注意以下几点:

  • 子线程的Looper.prepare() 函数的quitAllowed默认为true,表示MessageQueue允许退出;而主线程的 Looper.prepareMainLooper()里的Looper.prepare(false)函数的quitAllowed为false,表示MessageQueue不允许退出。这里的quitAllowed参数传递给MessageQueue对象,当调用MessageQueuec对象的quit函数时,会判断这个参数,如果是主线程,也就是quitAllowed参数为false时,会抛出异常。

    void quit(boolean safe) {
            if (!mQuitAllowed) {//主线程不可能退出消息队列
                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);
            }
        }
    
  • 主线程Looper初始化成功后,赋值给了成员变量sMainLooper,这个成员的作用就是向其他线程提供主线程的Looper对象。所以说不管在那里都可以通过调用Looper.getMainLooper()函数能获取主线程的Looper对象的原因。

  • 主线程的Handler的初始化。从源码上看出主线程的Handler作为ActivityThread的mH成员变量,直接final H mH = new H()。而H处理的事务涉及到Activity的启动流程,生命周期等等。(Activity的启动流程,生命周期就不展开讨论了,感觉兴趣的可以自行研究。)

    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();//初始化主线程的Handler
    }
    
    final H mH = new H()
    @UnsupportedAppUsage
    final Handler getHandler() {
        return mH;
    }
    

主线程的Looper、Handler都初始化完成后,调用Looper.loop()函数开始从MessageQueue中不断轮询,获取Message。我们从Looper.loop()函数分析。

Looper.loop()函数

在Looper.loop()函数不断轮询,是通过MessageQueue.next()函数不断轮询,取出对应的消息。源码如下:

public static void loop() {
    //通过sThreadLocal.get()获取刚刚初始化好的Looper对象
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    //获取Looper对应的消息队列MessageQueue
    final MessageQueue queue = me.mQueue;

    //...省略部分代码

    for (;;) {//不断循环从消息队列中取出消息
        Message msg = queue.next(); //有可能阻塞
        if (msg == null) {//没有消息,则退出消息队列
            // No message indicates that the message queue is quitting.
            return;
        }
        //...省略部分代码
        try {
            //msg.target就是Handler,把获取到的消息分发出去
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
            Trace.traceEnd(traceTag);
            }
        }
        //...省略部分代码
        msg.recycleUnchecked();//回收Message
    }
}

从queue.next()获取Message, 调用msg.target.dispatchMessage(msg)函数把Message分发出去,然后调用msg.recycleUnchecked()回收Message对象。

Message的创建

创建Message有两种方式:我们可以直接new Message(),也可以Message msg = Message.obtain(),当然Message类中还重写了很多obtain(xxx)函数。

Message m = new Message();
m.arg1=2;
m.obj="我是子线程的obj";
m.what=1;
mHandler.sendMessage(m);

Message mm = Message.obtain();
mm.obj="我是子线程的obj";
mm.what=1;
mHandler.sendMessage(mm);

既然可以new Message()创建对象,为什么还提供了Message.obtain()获取对象,其实这里涉及到对象复用设计(享元设计模式)。对象复用设计先放一放,下面再讨论分析

Handler发送Message

Handler类中定义了一系列发送Message的函数,我们就选取两个较为常用的作为切入点,进入mHandler.sendMessage(Message msg)或mHandler.sendEmptyMessage(1)的源码:

Message msg = new Message;
msg.what = 2;
mHandler.sendMessage(msg);//切入点一
mHandler.sendEmptyMessage(1);//切入点二

//切入点一
public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}
//切入点二
public final boolean sendEmptyMessage(int what){
    return sendEmptyMessageDelayed(what, 0);
}
//delayMillis 延时时间
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();//获取一个消息Message 为什么不直接new出来?下面会作出解答
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {//延时时间小于0毫秒,默认为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);
}

//最终调用enqueueMessage函数,把消息插入到消息队列中
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //this就是Handler Message中持有一个Handler
    //为发送消息出队列交给handler处理埋下伏笔。
    msg.target = this;
    if (mAsynchronous) {//是否是异步
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);//调用消息队列的入队函数
}

从源码分析得知,不管Handler调用sendXXX()还是postXXX()发送Message,都会调用Handler.enqueueMessage()函数,把当前的Handler对象赋值给msg.target,如果是异步,同时把Message的flags为异步,最终调用MessageQueue.enqueueMessage()函数,函数中的参数queue,是构造函数里初始化的;参数msg,是Message,参数uptimeMillis:是更新时间,如果不设置更新时间则以系统更新时间,否则使用设置的更新时间。下面用一张Handler发送消息的关系图片加深印象和解理:


MessageQueue类

MessageQueue.enqueueMessage函数

Handler发送消息时,最终调用MessageQueue类的 enqueueMessage函数。

//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
   synchronized (this) {
        if (mQuitting) {
            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;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            //是阻塞状态,Message没有绑定Handler,是异步,三者都成立时,则需要唤醒
            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;
        }
        if (needWake) {//调用Native的唤醒机制
            nativeWake(mPtr);
        }
    }
    return true;
}

当消息入队时,首先会判断,如果退出,则清空消息。如果是第一个消息、新消息没有延迟、新消息延迟时间小于队列第一个消息的,根据needWake是否调用nativeWake()函数,对这个消息进行处理(也就是唤醒MessageQueue.next()获取消息)。如果消息不为空并时间大于当前时间,会依次遍历消息队列,将消息按延迟时间插入消息队列适应的位置(按延迟时间从小到大排序插入消息)。从源码中我们可能看出, MessageQueue 实际上在里面维护着一个 Message 构成的单链表(因为只有next节点),每次插入Message都会按时间顺序进行插入, MessageQueue 中的 Message 都是按照时间排好序的,这样就使得循环取出 Message 的时候只需要一个个地从前往后拿即可,这样 Message 可以按时间先后顺序被消费,也表明MessageQueue是一个优先级队列。Handler的唤醒机制放一放,下面再讨论分析

MessageQueue.next函数

在Looper.loop函数中,一个死循环不断循环取到消息就会消费,不断循环取到消息其实是不断的从MessageQueue.next()轮询获取Message。

Message next() {
    //...省略部分代码
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        nativePollOnce(ptr, nextPollTimeoutMillis);//根据nextPollTimeoutMillis是阻塞还唤醒
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //优先处理异步信息(同步屏障机制)
            if (msg != null && msg.target == null) {
                // 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) {
                if (now < msg.when) {
                    // 当头消息延迟时间大于当前时间,阻塞消息要到延迟时间和当前时间的差值
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 取到一个消息
                    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 {
                nextPollTimeoutMillis = -1;//队列已无消息,一直阻塞
            }
        }
        //...省略部分代码
        //如果第一次空闲,则获取运行的待处理数。
        //只有当队列为空或队列中的第一条消息(可能是一个屏障)将来要处理时,才会运行空闲句柄。
        if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
        if (pendingIdleHandlerCount <= 0) {
             //没有要运行的空闲处理程序。一直阻塞
              mBlocked = true;
              continue;
         }

        //...省略部分代码
        pendingIdleHandlerCount = 0;
        nextPollTimeoutMillis = 0;
    }
}

Looper.loop轮询器,真正干活的是MessageQueue.next()函数,刚开始初始化Looper对象,轮询MessageQueue时,next函数获取Message时,MessageQueue队列中并没有Message,调用nativePollOnce(ptr, nextPollTimeoutMillis),进行阻塞,nextPollTimeoutMillis参数为-1。阻塞的处理在Native层处理。Handler的阻塞机制放一放,下面再讨论分析

如果消息不为null,同时msg.target为null,会一直不断遍历Message,直到遇到是异步标记,则取出消息。在这里我们都会觉得奇怪,Handler.enqueueMessage函数里msg.target=this,都已经赋值了,为什么msg.target还会为null呢,这是与Handler的同步屏障有关,这里先放一放,下面再讨论分析

判断如果当前消息是否到了应该发送的时间,如果到了应该发送的时间,就会将该消息取出并返回,否则仅仅只是将 nextPollTimeoutMillis 置为了剩余时长,这个时长与Integer.MAX_VALUE做了防止Int越界操作。nextPollTimeoutMillis 阻塞对应时长,时间到可被底层唤醒,获取消息并消费掉。

基于第一次运行MessageQueue 为空,或者消息待处理状态,mBlocked = true继续阻塞,则会尝试去执行一些 idleHandler,并在执行后将 pendingIdleHandlerCount 置为 0避免下次再次执行。

下面我们通过一张MessageQueue入队出队,加深印象:


Handler通过dispatchMessage分发处理Message

当Looper.loop()函数获取到一个Message,则调用Handler.dispatchMessage(msg)函数分发处理这消息。

public static void loop() {
    for (;;) {//不断循环从消息队列中取出消息
        Message msg = queue.next(); //有可能阻塞
        //...省略部分代码
        try {
            //msg.target就是Handler,把获取到的消息分发出去
            msg.target.dispatchMessage(msg);
        } finally { }
        //...省略部分代码
        msg.recycleUnchecked();//回收Message
    }
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //如果设置了回调,让实现Callback的实现类去处理这条消息
            //如果返回true,不处理Handler的handleMessage函数,否则处理Handler.handleMessage函数
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

Handler.dispatchMessage()函数是怎么分发处理消息的,从源码也得出,就是作为判断让谁去处理这条消息。如果msg.callback不为null,让Message里的callback处理,那么msg.callback是那里赋值的,上面Handler发送消息里重载了不同的发送消息的函数,就看Handler.post(Runnable r)函数源码作为切入点(其他切入点就不列举了),一切皆明白。

public final boolean post(Runnable r){
    return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();//从对象池里获取一条消息
    m.callback = r;
    return m;
}

Handler.Callback作用

msg.callback != null 其实msg.callback 就是一个Runnable,交给了实现Runnable的类去处理这条消息。

mCallback != null ,如果设置了Handler.mCallback 回调,mCallback.handleMessage(msg)返回true,让实现Handler.Callback的实现类去处理这条消息,而Handler.handleMessage()则不处理这条消息,如果返回是false,那么Handler.handleMessage()也处理这条消息。

Handler handler = new Handler(Looper.getMainLooper(),callback){
    @Override
    public void handleMessage(@NonNull Message msg) {
        Log.e("lu", "Handler handleMessage");
        super.handleMessage(msg);
    }
};

Message msg = new Message();
msg.what=1;
handler.sendMessage(msg);

Handler.Callback callback = new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message msg) {
        Log.e("lu", "callback handleMessage");
        return true;//返回为true,不会执行Handler handleMessage方法,返回为false会执行
    }
};

注意:Handler.Callback.handleMessage,返回为true,不会执行Handler handleMessage方法,返回为false会执行。

Handler机制组成成员

从Handler的构造函数和Handler发送Message这两步得出,与Handler机制组成成员类有:Handler、Message、MessageQueue、Looper。下面分析这些成员之间的关系。

Handler获取当前线程的Looper对象,Looper是用来从存放Message的MessageQueue中取出Message,接着Handler进行Message分发处理。

  • Handler:是消息(Message)的处理者,负责消息的发送,和接收消息的处理。
  • Message:消息,可以理解为线程间交流的数据。
  • Looper:是每个线程里的消息队列(MessageQueue)管家一样,用于轮询消息队列,一个线程只有一个Looper。
  • MessageQueue:消息队列是存储消息和管理消息。

Handler机制组成成员之间的关系

Handler与Message之间的关系

public final class Message implements Parcelable {
    Handler target;
}
public class Handler{
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
}

从源代码可以看出,创建一个Message对象时可以设置其成员target,进行Handler与Message绑定,如果创建Message对象时不绑定,那么Handler发送Message时,调用enqueueMessage函数时,自动进行绑定。

Handler与Looper之间的关系

public class Handler{
    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()");
        }
    }
}

public final class Looper{
   public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
}

从源码分析得出,在ThreadLoocal本地变量中取出一个Looper对象,在Handler结构函数里,将当前线程的Looper对象赋值给Handler成员mLooper,实行绑定。

Looper、Thread、MessageQueue之间的关系

public final class Looper{
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    public static void prepare() {
        prepare(true);
    }
    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));
    }
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
}

从Looper的构造函数看出,直接创建一个MessageQueue对象并赋值给Looper.mQueue,同时当前线程赋值给Looper.mThread,然而Looper对象是使用ThreadLocal本地变量存放,ThreadLocal的使用是线程隔离,因此Thread、Looper、MessageQueue是1:1:1关系,也就是一个Looper对象绑定一个MessageQueue和一个线程。关于ThreadLocal讨论放一放,下面再讨论分析

MessageQueue与Message之间的关系

public class Hander{
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //...省略部分代码 
        return queue.enqueueMessage(msg, uptimeMillis);
    }
}

public final class MessageQueue{
    boolean enqueueMessage(Message msg, long when) {
        //...省略部分代码 
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
            //...省略部分代码     
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
               //...省略部分代码 
                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;
            }
        //...省略部分代码 
        }
        return true;
    }
}

从Handler发送Message,最后调用MessageQueue.enqueueMessage函数,可以看得出,MessageQueue类中持有一个Messages成员变量mMessages,然后enqueueMessage函数里Message p = mMessages,最后将传入的Message存放到MessageQueue.mMessages中。也就是说MessageQueue是存放Message的一个容器。

Handler机制原理

通过上述源码分析Handler机制过程中,我们很容易得出Handler机制的原理主线路:从Handler类 -->绑定一个Looper和MessageQueue --> Handler发送Message -->Message绑定Handler --> MessageQueue保存Message --> Looper.loop一直在轮询 --> MessageQueue取出Message --> Handler.dispatchMessage分发消息 --> Handler处理消息 Handler.HandleMessage

通过画一张Handler机制流程图,加深印象,更容易理解。


Handler机制的难点

由于文章边幅太长,不利于阅读,关于Handler以下的的难点,另写一篇文章:

  • prepare()函数中,使用ThreadLocal存放Looper对象,ThreadLocal的作用。
  • Message使用对象复用池(享元设计模式)
  • 内存共享(如何切换线程的)
  • Handler的阻塞/唤醒机制
  • Handler的同步屏障

总结

  • Handler的简单使用,注意内存泄漏风险。
  • 弄清楚Handler机制的构成成员
  • 弄清楚Handler机制的构成成员之间的调用过程
  • 理清楚Handler机制成员之间的关系
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343