Binder+Handler,看元件生命週期如何被響應

MDove發表於2018-10-15

前言

上文我們聊了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+Handler,看元件生命週期如何被響應

對於上圖來說。程式倆端,一個是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()方法。流程是這樣的:

Binder+Handler,看元件生命週期如何被響應

用文字簡單解釋一下: 通過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面試填坑小分隊

相關文章