从源码解析Handler机制

Handler机制是面试中的常客了,今天和大家一起通过源码层面来解析一下。

前提知识点

Handler机制涉及到几个类: MessageQueue, Looper, Message, ActivityThread。

  • ActivityThread: 主线程,开启loop循环,管理application进程, 调度管理activity, 广播及其他操作。
  • Message: handler机制中消息的载体,包含相关描述和数据对象,包含属性what, obj, arg1,arg2等。
  • MessageQueue: 用于存储 Message,内部维护了 Message 的链表,每次拿取 Message 时,若该 Message 离真正执行还需要一段时间,会通过 nativePollOnce 进入阻塞状态,避免资源的浪费。若存在消息屏障,则会忽略同步消息优先拿取异步消息,从而实现异步消息的优先消费。
  • Looper:一个用于遍历 MessageQueue 的类,每个线程有一个独有的 Looper,它会在所处的线程开启一个死循环,不断从 MessageQueue 中拿出消息,并将其发送给 target 进行处理

AppCompatActivity.java-> FragmentActivity.java->SupportActivity.java->Activity.java

在Activity中默认有一个ActivityThread这是一个main thread,其中final Looper mLooper = Looper.myLooper();实例化了一个looper对象。
在ActivityThread.java中的main方法中,有如下代码

public static void main(String[] args) {
   		...省略其他代码
        Looper.prepareMainLooper();
      	...省略其他代码
        Looper.loop();
    }
Looper.java

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

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));
    }
  //  Looper构造函数中初始了消息队列MessageQueue对象
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

创建looper 对象之前,会判断 sThreaLocal 中是否已经绑定过 Looper 对象,如果是则抛出异常,确保一个线程中Looper.prepare()只调用一次。
如果在MainActivity中调用,如下代码,会报错。
在这里插入图片描述
在这里插入图片描述

通过上述代码可以得知,Activity中默认开始了loop()循环,用于获取handler发送的消息。

最简单的应用

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private final String TAG = "MainActivity";
    public final int MSG_DOWN_FAIL = 1;
    public final int MSG_DOWN_SUCCESS = 2;
    public final int MSG_DOWN_START = 3;

    @BindView(R.id.btn_start_thread)
    Button btnStart;

    @BindView(R.id.tv_status)
    TextView tvShow;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DOWN_START:
                    tvShow.setText("down start");
                    break;
                case MSG_DOWN_SUCCESS:
                    tvShow.setText("down success");
                    break;
                case MSG_DOWN_FAIL:
                    tvShow.setText("down fail");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        btnStart.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_start_thread:
                new MyThread().start();
                break;
            default:
                break;
        }
    }

    class MyThread extends Thread {

        @Override
        public void run() {
            handler.sendEmptyMessage(MSG_DOWN_START);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Message msg = Message.obtain();
            msg.what = MSG_DOWN_SUCCESS;
            handler.sendMessage(msg);
        }
    }
}

sendMessage之后发生了什么

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

sendMessage调用了sendMessageDelayed->sendMessageAtTime->enqueueMessage, 最终调用了MessageQueue的enqueueMessage的方法, 并将自己设置为message的target

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

接下来就到了消息队列MessageQueue中了,来看一下在这里插入图片描述

  • 如果message的target为null,则直接抛出异常
  • 按照message的时间when有序插入到MessageQueue中,所以MessageQueue是一个按时间排序的有序队列。

怎么取MessageQueue中的消息

其实这里就是Looper.loop()方法从消息队列中不断循环取消息了。
在这里插入图片描述
不断调用MessageQueue的next()方法取消息,如果message不为null, 则调用handler的dispatchMessage分发消息。
在这里插入图片描述
在这里插入图片描述
这个handleMessage方法就是在创建Handler中覆盖的方法。

至此 Handler 的发送消息和消息处理流程已经介绍完毕。

面试常见问题

1.Looper.loop() 为什么不会阻塞主线程

Android的Ui线程,开启了一个死循环,但是并没有阻塞主线程是为什么呢?
在MessageQueue的next方法中有这样一行代码
在这里插入图片描述

private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/

nativePollOnce 方法是一个 native 方法,当调用此 native 方法时,主线程会释放 CPU 资源进入休眠状态,直到下条消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作,这里采用的 epoll 机制。

2.Handler 的 sendMessageDelayed 或者 postDelayed 是如何实现的

messageQueue的enqueueMessage是按照消息的when时间有序加入到队列的,取的时候也是按照时间进行取的
在这里插入图片描述
可以看到如果当前时间小于msg设置的时间,会计算一个timeout,在timeout到了之后,才会将UI线程唤醒,交由CPU执行。

总结

1.APP启动创建主线程的时候会通过ActivityThread执行Looper.prepare(),创建一个looper 对象,在私有的构造方法中又创建了 MessageQueue 作为此 Looper 对象的成员变量,Looper 对象通过 ThreadLocal 绑定 MainThread 中。
2.在创建Handler对象的时候,通过构造函数获取ThreadLocal 绑定的looper对象,并通过looper获取消息队列MessageQueue作为成员变量
3.子线程发送消息时,将msg的target设置为handler自身,之后调用成员MessageQueue的enqueueMessage将消息按照msg.when时间排序插入到消息队列
4.主线程通过Looper.loop()开启不阻塞UI线程的死循环,通过绑定的looper对象获取MessageQueue,调用next()方法不断获取msg, 并通过(handler)msg.target.dispatchMessage发送至我们创建的handler时覆盖的handleMessage()方法

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页