Android Handler 详解

2,770 阅读5分钟

读前思考

学习一门技术或者看一篇文章最好的方式就是带着问题去学习,这样才能在过程中有茅塞顿开、灯火阑珊的感觉,记忆也会更深刻。

  1. 如何获取 Message 进行消息发送
  2. 使用 handler 造成内存泄露,如何避免?
  3. handler looper messageQueue的关系
  4. 程序是如何区分是将消息发给哪个 Handler ?

概述

做 Android 开发肯定离不开跟 Handler 打交道,它通常被我们用来做主线程与子线程之间的通信工具,而 Handler 作为 Android 中消息机制的重要一员也确实给我们的开发带来了极大的便利。

可以说只要有异步线程与主线程通信的地方就一定会有 Handler

1、 Handler 的基本使用

1.1 创建 Handler

Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露

这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。

解决该问题的最有效的方法是:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息

示例如下:

public class Part8HandlerActivity extends AppCompatActivity {

    private Button bt_handler_send;

    private static class MyHandler extends Handler {

        //弱引用持有Part8HandlerActivity , GC 回收时会被回收掉
        private WeakReference<Part8HandlerActivity> weakReference;

        public MyHandler(Part8HandlerActivity activity) {
            this.weakReference = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            Part8HandlerActivity activity = weakReference.get();
            super.handleMessage(msg);
            if (null != activity) {
                //执行业务逻辑
                Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
            }
        }
    }


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

        //创建 Handler
        final MyHandler handler = new MyHandler(this);

        bt_handler_send = findViewById(R.id.bt_handler_send);
        bt_handler_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //使用 handler 发送空消息
                        handler.sendEmptyMessage(0);

                    }
                }).start();
            }
        });
    }
    
    @Override
    protected void onDestroy() {
        //移除所有回调及消息
        myHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
}

注意:单纯的在 onDestroy 移除消息并不保险,因为 onDestroy 并不一定执行。

1.2 Message 获取

获取 Message 大概有如下几种方式:

Message message = myHandler.obtainMessage(); //通过 Handler 实例获取
Message message1 = Message.obtain();    //通过 Message 获取
Message message2 = new Message();       //直接创建新的 Message 实例

通过查看源码可知,Handler 的 obtainMessage() 方法也是调用了 Message 的 obtain() 方法

public final Message obtainMessage()
{
    return Message.obtain(this);
}

通过查看 Message 的 obtain 方法

public static Message obtain(Handler h) {
        //调用下面的方法获取 Message
        Message m = obtain();
        //将当前 Handler 指定给 message 的 target ,用来区分是哪个 Handler 的消息
        m.target = h;

        return m;
    }
    
//从消息池中拿取 Message,如果有则返回,否则创建新的 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();
    }

为了节省开销,我们在使用的时候尽量复用 Message,使用前两种方式进行创建。

1.3 Handler 发送消息

Handler 提供了一些列的方法让我们来发送消息,如 send()系列 post()系列 。

不过不管我们调用什么方法,最终都会走到 MessageQueue.enqueueMessage(Message,long) 方法。 以 sendEmptyMessage(int) 方法为例:

//Handler
sendEmptyMessage(int)
  -> sendEmptyMessageDelayed(int,int)
    -> sendMessageAtTime(Message,long)
      -> enqueueMessage(MessageQueue,Message,long)
  			-> queue.enqueueMessage(Message, long);

从中可以发现 MessageQueue 这个消息队列,负责消息的入队,出队。

2、Handler 原理解析

1. 构造函数

实际上我们在实例化 Handler 的时候 Handler 会去检查当前线程的 Looper 是否存在,如果不存在则会报异常,也就是说在创建 Handler 之前一定需要先创建 Looper

public Handler(Callback callback, boolean async) {
     //检查当前线程是否持有 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 持有一个 MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

2. Looper

通过调用 Looper.prepare() 可以在当前线程创建 Looper,然后调用 Looper.loop() 让消息队列循环起来。

代码如下:

private static void prepare(boolean quitAllowed) {
    
    //如果当前线程已经存在 Looper,则会抛出异常
    //在主线程中调用 prepare 就会抛出此异常,因为主线程已经存在 Looper
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //将创建的 Looper 对象存入线程的 ThreadLocal 中,保持唯一
    sThreadLocal.set(new Looper(quitAllowed));
}

Looper.loop() 相关代码

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.");
        }
        //然后获取消息队列
        final MessageQueue queue = me.mQueue;

        //..
        for (;;) {
            //不断从 MessageQueue 中获取消息
            Message msg = queue.next(); // might block
            //
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //..
            try {
            // 通过 handler 发送消息
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                //..
            }
            //..
            //回收 Message
            msg.recycleUnchecked();
        }
    }

3. 小结

Handler 的背后有着 Looper 以及 MessageQueue 的协助,三者通力合作,分工明确。 尝试小结一下它们的职责,如下:

Looper :负责关联线程以及消息的分发在该线程下从 MessageQueue 获取 Message,分发给 Handler ;
MessageQueue :是个队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message ;
Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。

Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。

线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决定。

3、 总结

Handler 简单易用的背后藏着工程师大量的智慧,要努力向他们学习。

希望看完本文能加深你对 Handler 的理解,对接下来学习有所帮助。

4、 参考

Handler

Android消息机制1-Handler(Java层)

Handler 都没搞懂,拿什么去跳槽啊?