相关文章推荐
一直单身的抽屉  ·  Exception in thread ...·  3 年前    · 
礼貌的海龟  ·  MySql ...·  3 年前    · 

上文我们聊了Handler机制出现的道理,以及消息队列的写法。

全面理解Handler-1:理解消息队列,手写消息队列

今天我们来聊一聊上文中还未作出解答的问题。作为死循环的loop,如何做到不阻塞住我们的生命周期。( ~文末有福利~ )

一、死循环

首先,我们得弄清死循环的意义。我们都明白,对于线程来说。代码的结束也就意味的线程生命的终止。想要获得“永生”,那么该怎么做?很显然,只要代码永远执行不完就可以了。既然如此,那么死循环显然非常符合这个要求:

while(true){
	// 我是不朽的~!
复制代码

这样我们就弄清楚了loop()死循环的意义之一。 但是随之而来,引入了全新的问题:既然是死循环,那么循环体之外的代码势必不会被执行。

为了能够正常的运行程序,我们就要想办法在循环体内调度其他方法。start其他线程,通过消息队列轮询。这显然是一个不错的方案,主线程的循环体只需要源源不断的从消息队列中取消息,执行就可以了。至于消息从哪里来,从哪个线程来这个不重要,一点都不重要。

Handler机制的一个作用就出来了:实现不同线程间的数据传递(这里传递是Message,其他线程往MessageQueue发送Message,主线程轮询取出并执行)。

有了这个思想,我们来想想对于Android来说是一个怎样的实现。

1.1、Android中的死循环

显然ActivityThread中,main方法里调用的loop()就是这个死循环体。

public static void loop() {
	//省略部分代码
    for (;;) {
	    //省略部分代码
复制代码

有了上边的分析,我们就能够明白:

  • 1、它的存在保证了我们主线程的不死不灭。
  • 2、而Handler机制保证了我们线程之间消息的调度。
  • 死循环的意义我们明白了。那么死循环为什么阻塞不住我们的UI线程?一句话先来概括一下:

    真正会卡死主线程的操作是,在onCreate/onStart/onResume等这种生命周期方法中执行了耗时操作,而非loop死循环。 只要我们组件的生命周期正常被回调,那么loop死不死循环,对我们没有任何影响。因为我们的代码逻辑全部是在对应的生命周期中执行了。

    那么问题就来了:**组件的生命周期方法是怎么被回调的?**回答这个问题之前,让我们好好捋一捋Handler机制的设计:

    1.2、Handler机制的思路

    主线程持有MessageQueue,loop()死循环从MessageQueue中不断的取出Message,不为null,就执行Message中对应的方法。其他线程通过LocalThread拿到MessageQueue,进而就可以往Queue中发送Message。

    有了这个思路其实我们就可以回答这个问题: 组件的生命周期方法是怎么被回调的?

    答案是:只需要在生命周期方法该被回调的时候,在 其他线程 中,通过往MessageQueue中发送Message的形式,就可以告知主线程,该回调对应的生命周期方法了。

    当然用文字表达很简单。实际落实到代码中就涉及很多内容了。因此接下来让我们真正的从代码中看生命周期方法是如何被调度的。

    二、生命周期的调度

    2.1、简述流程

    要想弄清楚生命周期的调度,这里不得不提起一个名词:Binder机制。而且这其中还涉及到了我们一定耳濡目染过的流程,先看一张图:

    对于上图来说。进程俩端,一个是Binder客户端一个是Binder服务端。简单来说,服务端用于响应客户端的调用。

    因此对于上图来说,App默认进程中的ApplicationThread就是Binder的客户端,用于响应system_server进程中Binder客户端ApplicationThreadProxy的调用。

    同理,system_server进程中Binder客户端ActivityManagerService就是响应App默认进程中Binder客户端的ActivityManagerProxy对象的调用。

    2.2、Handler + Binder

    在2.1中我们用一张图一段话聊了Binder机制。有了这个基础,我们就可以在结合Handler机制,解释一下我们的生命周期是如何被调度的了。

    对于Activity来说,它的生命周期一定是在主线程中被回调。那么此过程就是通过Handler机制的方式实现的。我们简单上一下ActivityThread中的一段代码,后文会着重分析:

    private class H extends Handler {
        // 省略部分代码
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case RESUME_ACTIVITY:
                    handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true);
                    break;
    复制代码

    最开始我们分析了,Handler的轮询在loop这个死循环里。想要保证源源不断的Message那么势必要启动额外的线程。而我们的ActivityManagerProxy就是额外的线程。它在loop()调用前被启动:

    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread(); 
    //建立Binder通道,也就是创建新线程
    thread.attach(false);
    Looper.loop();
    复制代码

    简单来说,比如我们想要执行某Activity的onResume()方法。流程是这样的:

    用文字简单解释一下: 通过 ActivityManagerProxy (App默认进程),通过Binder传输到 ActivityManagerService 中, ActivityManagerService 再通过同进程的 ApplicationThreadProxy (system_server进程)通过Binder传输到 ApplicationThread (App默认进程)之中,此时已经来到了我们的App默认进程中的BInder线程中。那么此时就可以通过Handler机制,将Message发送给主线程了,再由主线程找到对应的Activity实例,回调对应的onResume方法。

    三、Read F**k Code

    3.1、Binder线程的启动

    我们先来看一下上文提到的attch方法:

    private void attach(boolean system) {
        final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
    复制代码

    IActivityManager是IActivityManager,aidl的接口类。那它具体的对象是谁?

    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    private static final Singleton<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @Override
            protected IActivityManager create() {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am;
    复制代码

    很明显我们需要追到ServiceManager中一探究竟:

    private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
    public static IBinder getService(String name) {
    	// 省略部分代码
    	IBinder service = sCache.get(name);
    复制代码

    我们可以看到这里通过HashMap去查多赢的IBinder对象。那我们就来看看这个key为Context.ACTIVITY_SERVICE的value是谁:

    ActivityManagerService{
    	// 省略部分代码
    	ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
    复制代码

    那么此时我们也就知道IActivityManager 对象具体是谁了,真相只有一个ActivityManagerProxy。因为 IActivityManager.Stub.asInterface(b) 会根据当前进程情况来决定是返回Stub对象还是Proxy对象。我们的AMS运行在另一个进程那么此时返回的就是Proxy对象。

    此时,我们的额外的线程就创建完毕了。有了这个线程我们就可以来在loop外去相应四大组件的生命周期回调了。

    3.2、H

    因为本篇的重点是Handler里,所以这里跳过Binder的过程,我们直接假设,ApplicationThreadProxy通过Binder调用了我们的ApplicationThread对象。

    private class ApplicationThread extends IApplicationThread.Stub {
    	// 省略部分代码
    	public final void scheduleResumeActivity(IBinder token, int processState,
                    boolean isForward, Bundle resumeArgs) {
            // 省略部分代码
            sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0, 0, seq);
    复制代码

    此时我们可以很清晰的看到,在 scheduleResumeActivity 方法中,使用了sendMessage方法,很明显这是一个封装的方法。让我们进去看那一看:

    private void sendMessage(int what, Object obj, int arg1, int arg2, int seq) {
            if (DEBUG_MESSAGES) Slog.v(
                    TAG, "SCHEDULE " + mH.codeToString(what) + " arg1=" + arg1 + " arg2=" + arg2 +
                            "seq= " + seq);
            Message msg = Message.obtain();
            // 省略部分代码
            mH.sendMessage(msg);
    复制代码

    这里我们能够看到一个特别的Handler实现类H。说白了就是主线程中声明的一个Handler而已

    private class H extends Handler {
        // 省略部分代码
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case RESUME_ACTIVITY:
                    handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true);
                    break;
    复制代码

    此时我们就能够看到,我们Activity的生命周期完成了调用。

    final void handleResumeActivity(IBinder token,
                boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
            ActivityClientRecord r = mActivities.get(token);
            // 省略部分代码
            r = performResumeActivity(token, clearHide, reason);
    复制代码

    四、总结

    我所疑惑的点,无非是下面这几个。现在已经解释的通了。

    4.1、loop死循环的意义

    对于线程来说,代码执行结束,线程消失。那么对于主线程来说,想要不死,死循环首选。

    4.2、死循环如何保证声明周期的调度

    这里主要是通过Binder机制。在loop被调用前,会启动一个Binder线程。当我们想要调用某个组件的生命周期方法时,通过Binder机制,交由ActivityManagerService(AMS)去处理,最终仍通过Binder机制回传给我们的ApplicationThread对象。这个对象最终听过Handler将调度的Message发送到主线程的MessageQueue里,完成生命周期的正常调度。

    到此关于我对Handler想写的内容就正式结束了, 当然这其中仍有很多的坑还没有填

    比如: Linux pipe/epoll机制

    比如:篇幅比较长的 Binder ,关于Binder会在接下来的文章中出现。

    此外这篇文中分析的过程更多的有点像Activity的启动过程。接下来关于Binder的文章,有真正的以启动Activity为例子,把Binder机制串起来~

    哈哈,没有福利~

    我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,以及我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~