Binder+Handler,看组件生命周期如何被响应

2,180 阅读8分钟

前言

上文我们聊了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机制串起来~

哈哈,没有福利~

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

个人公众号:IT面试填坑小分队