1. 程式人生 > >Activity啟動過程全解析

Activity啟動過程全解析

文章出自:https://www.jianshu.com/p/6037f6fda285

前言

  • 一個App是怎麼啟動起來的?
  • App的程式入口到底是哪裡?
  • Launcher到底是什麼神奇的東西?
  • 聽說還有個AMS的東西,它是做什麼的?
  • Binder是什麼?他是如何進行IPC通訊的?
  • Activity生命週期到底是什麼時候呼叫的?被誰呼叫的?
  • 等等...

你是不是還有很多類似的疑問一直沒有解決?沒關係,這篇文章將結合原始碼以及大量的優秀文章,站在巨人的肩膀上,更加通俗的來試著解釋一些問題。但是畢竟原始碼繁多、經驗有限,文中不免會出現一些紕漏甚至是錯誤,還懇請大家指出,互相學習。

學習目標

  1. 瞭解從手機開機第一個zygote程序建立,到點選桌面上的圖示,進入一個App的完整流程,並且從原始碼的角度瞭解到一個Activity的生命週期是怎麼回事
  2. 瞭解到ActivityManagerServices(即AMS)、ActivityStack、ActivityThread、Instrumentation等Android framework中非常重要的基礎類的作用,及相互間的關係
  3. 瞭解AMS與ActivityThread之間利用Binder進行IPC通訊的過程,瞭解AMS和ActivityThread在控制Activity生命週期起到的作用和相互之間的配合
  4. 瞭解與Activity相關的framework層的其他瑣碎問題

寫作方式

這篇文章我決定採用一問一答的方式進行。

其實在這之前,我試過把每個流程的程式碼呼叫過程,用貼上原始碼的方式寫在文章裡,但是寫完一部分之後,發現由於程式碼量太大,整篇文章和老太太的裹腳布一樣——又臭又長,雖然每個重要的操作可以顯示出詳細呼叫過程,但是太關注於細節反而導致從整體上不能很好的把握。所以在原來的基礎之上進行了修改,對關鍵的幾個步驟進行重點介紹,力求語言簡潔,重點突出,從而讓大家在更高的層次上對framework層有個認識,然後結合後面我給出的參考資料,大家就可以更加快速,更加高效的瞭解這一塊的整體架構。

主要物件功能介紹

我們下面的文章將圍繞著這幾個類進行介紹。可能你第一次看的時候,印象不深,不過沒關係,當你跟隨者我讀完這篇文章的時候,我會在最後再次列出這些物件的功能,相信那時候你會對這些類更加的熟悉和深刻。

  • ActivityManagerServices,簡稱AMS,服務端物件,負責系統中所有Activity的生命週期
  • ActivityThread,App的真正入口。當開啟App之後,會呼叫main()開始執行,開啟訊息迴圈佇列,這就是傳說中的UI執行緒或者叫主執行緒。與ActivityManagerServices配合,一起完成Activity的管理工作
  • ApplicationThread,用來實現ActivityManagerService與ActivityThread之間的互動。在ActivityManagerService需要管理相關Application中的Activity的生命週期時,通過ApplicationThread的代理物件與ActivityThread通訊。
  • ApplicationThreadProxy,是ApplicationThread在伺服器端的代理,負責和客戶端的ApplicationThread通訊。AMS就是通過該代理與ActivityThread進行通訊的。
  • Instrumentation,每一個應用程式只有一個Instrumentation物件,每個Activity內都有一個對該物件的引用。Instrumentation可以理解為應用程序的管家,ActivityThread要建立或暫停某個Activity時,都需要通過Instrumentation來進行具體的操作。
  • ActivityStack,Activity在AMS的棧管理,用來記錄已經啟動的Activity的先後關係,狀態資訊等。通過ActivityStack決定是否需要啟動新的程序。
  • ActivityRecord,ActivityStack的管理物件,每個Activity在AMS對應一個ActivityRecord,來記錄Activity的狀態以及其他的管理資訊。其實就是伺服器端的Activity物件的映像。
  • TaskRecord,AMS抽象出來的一個“任務”的概念,是記錄ActivityRecord的棧,一個“Task”包含若干個ActivityRecord。AMS用TaskRecord確保Activity啟動和退出的順序。如果你清楚Activity的4種launchMode,那麼對這個概念應該不陌生。

主要流程介紹

下面將按照App啟動過程的先後順序,一問一答,來解釋一些事情。

讓我們開始吧!

zygote是什麼?有什麼作用?

首先,你覺得這個單詞眼熟不?當你的程式Crash的時候,列印的紅色log下面通常帶有這一個單詞。

zygote意為“受精卵“。Android是基於Linux系統的,而在Linux中,所有的程序都是由init程序直接或者是間接fork出來的,zygote程序也不例外。

在Android系統裡面,zygote是一個程序的名字。Android是基於Linux System的,當你的手機開機的時候,Linux的核心載入完成之後就會啟動一個叫“init“的程序。在Linux System裡面,所有的程序都是由init程序fork出來的,我們的zygote程序也不例外。

我們都知道,每一個App其實都是

  • 一個單獨的dalvik虛擬機器
  • 一個單獨的程序

所以當系統裡面的第一個zygote程序執行之後,在這之後再開啟App,就相當於開啟一個新的程序。而為了實現資源共用和更快的啟動速度,Android系統開啟新程序的方式,是通過fork第一個zygote程序實現的。所以說,除了第一個zygote程序,其他應用所在的程序都是zygote的子程序,這下你明白為什麼這個程序叫“受精卵”了吧?因為就像是一個受精卵一樣,它能快速的分裂,並且產生遺傳物質一樣的細胞!

SystemServer是什麼?有什麼作用?它與zygote的關係是什麼?

首先我要告訴你的是,SystemServer也是一個程序,而且是由zygote程序fork出來的。

知道了SystemServer的本質,我們對它就不算太陌生了,這個程序是Android Framework裡面兩大非常重要的程序之一——另外一個程序就是上面的zygote程序。

為什麼說SystemServer非常重要呢?因為系統裡面重要的服務都是在這個程序裡面開啟的,比如
ActivityManagerService、PackageManagerService、WindowManagerService等等,看著是不是都挺眼熟的?

那麼這些系統服務是怎麼開啟起來的呢?

在zygote開啟的時候,會呼叫ZygoteInit.main()進行初始化

public static void main(String argv[]) {
    
     ...ignore some code...
    
    //在載入首個zygote的時候,會傳入初始化引數,使得startSystemServer = true
     boolean startSystemServer = false;
     for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = true;
                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                    abiList = argv[i].substring(ABI_LIST_ARG.length());
                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
                } else {
                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
                }
            }
            
            ...ignore some code...
            
         //開始fork我們的SystemServer程序
     if (startSystemServer) {
                startSystemServer(abiList, socketName);
         }

     ...ignore some code...

}

我們看下startSystemServer()做了些什麼

    /**留著這個註釋,就是為了說明SystemServer確實是被fork出來的
     * Prepare the arguments and fork for the system server process.
     */
    private static boolean startSystemServer(String abiList, String socketName)
            throws MethodAndArgsCaller, RuntimeException {
        
         ...ignore some code...
        
        //留著這段註釋,就是為了說明上面ZygoteInit.main(String argv[])裡面的argv就是通過這種方式傳遞進來的
        /* Hardcoded command line to start the system server */
        String args[] = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
            "--capabilities=" + capabilities + "," + capabilities,
            "--runtime-init",
            "--nice-name=system_server",
            "com.android.server.SystemServer",
        };

        int pid;
        try {
            parsedArgs = new ZygoteConnection.Arguments(args);
            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

        //確實是fuck出來的吧,我沒騙你吧~不對,是fork出來的 -_-|||
            /* Request to fork the system server process */
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.debugFlags,
                    null,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }

        /* For child process */
        if (pid == 0) {
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            handleSystemServerProcess(parsedArgs);
        }

        return true;
    }

ActivityManagerService是什麼?什麼時候初始化的?有什麼作用?

ActivityManagerService,簡稱AMS,服務端物件,負責系統中所有Activity的生命週期。

ActivityManagerService進行初始化的時機很明確,就是在SystemServer程序開啟的時候,就會初始化ActivityManagerService。從下面的程式碼中可以看到

public final class SystemServer {

    //zygote的主入口
    public static void main(String[] args) {
        new SystemServer().run();
    }

    public SystemServer() {
        // Check for factory test mode.
        mFactoryTestMode = FactoryTest.getMode();
    }
    
    private void run() {
        
        ...ignore some code...
        
        //載入本地系統服務庫,並進行初始化 
        System.loadLibrary("android_servers");
        nativeInit();
        
        // 建立系統上下文
        createSystemContext();
        
        //初始化SystemServiceManager物件,下面的系統服務開啟都需要呼叫SystemServiceManager.startService(Class<T>),這個方法通過反射來啟動對應的服務
        mSystemServiceManager = new SystemServiceManager(mSystemContext);
        
        //開啟服務
        try {
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        }
       
        ...ignore some code...
    
    }

    //初始化系統上下文物件mSystemContext,並設定預設的主題,mSystemContext實際上是一個ContextImpl物件。呼叫ActivityThread.systemMain()的時候,會呼叫ActivityThread.attach(true),而在attach()裡面,則建立了Application物件,並呼叫了Application.onCreate()。
    private void createSystemContext() {
        ActivityThread activityThread = ActivityThread.systemMain();
        mSystemContext = activityThread.getSystemContext();
        mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
    }

    //在這裡開啟了幾個核心的服務,因為這些服務之間相互依賴,所以都放在了這個方法裡面。
    private void startBootstrapServices() {
        
        ...ignore some code...
        
        //初始化ActivityManagerService
        mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        
        //初始化PowerManagerService,因為其他服務需要依賴這個Service,因此需要儘快的初始化
        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);

        // 現在電源管理已經開啟,ActivityManagerService負責電源管理功能
        mActivityManagerService.initPowerManagement();

        // 初始化DisplayManagerService
        mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
    
    //初始化PackageManagerService
    mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
       mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    
    ...ignore some code...
    
    }

}

經過上面這些步驟,我們的ActivityManagerService物件已經建立好了,並且完成了成員變數初始化。而且在這之前,呼叫createSystemContext()建立系統上下文的時候,也已經完成了mSystemContext和ActivityThread的建立。注意,這是系統程序開啟時的流程,在這之後,會開啟系統的Launcher程式,完成系統介面的載入與顯示。

你是否會好奇,我為什麼說AMS是服務端物件?下面我給你介紹下Android系統裡面的伺服器和客戶端的概念。

其實伺服器客戶端的概念不僅僅存在於Web開發中,在Android的框架設計中,使用的也是這一種模式。伺服器端指的就是所有App共用的系統服務,比如我們這裡提到的ActivityManagerService,和前面提到的PackageManagerService、WindowManagerService等等,這些基礎的系統服務是被所有的App公用的,當某個App想實現某個操作的時候,要告訴這些系統服務,比如你想開啟一個App,那麼我們知道了包名和MainActivity類名之後就可以開啟

Intent intent = new Intent(Intent.ACTION_MAIN);  
intent.addCategory(Intent.CATEGORY_LAUNCHER);              
ComponentName cn = new ComponentName(packageName, className);              
intent.setComponent(cn);  
startActivity(intent); 

但是,我們的App通過呼叫startActivity()並不能直接開啟另外一個App,這個方法會通過一系列的呼叫,最後還是告訴AMS說:“我要開啟這個App,我知道他的住址和名字,你幫我開啟吧!”所以是AMS來通知zygote程序來fork一個新程序,來開啟我們的目標App的。這就像是瀏覽器想要開啟一個超連結一樣,瀏覽器把網頁地址傳送給伺服器,然後還是伺服器把需要的資原始檔傳送給客戶端的。

知道了Android Framework的客戶端伺服器架構之後,我們還需要了解一件事情,那就是我們的App和AMS(SystemServer程序)還有zygote程序分屬於三個獨立的程序,他們之間如何通訊呢?

App與AMS通過Binder進行IPC通訊,AMS(SystemServer程序)與zygote通過Socket進行IPC通訊。

那麼AMS有什麼用呢?在前面我們知道了,如果想開啟一個App的話,需要AMS去通知zygote程序,除此之外,其實所有的Activity的開啟、暫停、關閉都需要AMS來控制,所以我們說,AMS負責系統中所有Activity的生命週期。

在Android系統中,任何一個Activity的啟動都是由AMS和應用程式程序(主要是ActivityThread)相互配合來完成的。AMS服務統一排程系統中所有程序的Activity啟動,而每個Activity的啟動過程則由其所屬的程序具體來完成。

這樣說你可能還是覺得比較抽象,沒關係,下面有一部分是專門來介紹AMS與ActivityThread如何一起合作控制Activity的生命週期的。

Launcher是什麼?什麼時候啟動的?

當我們點選手機桌面上的圖示的時候,App就由Launcher開始啟動了。但是,你有沒有思考過Launcher到底是一個什麼東西?

Launcher本質上也是一個應用程式,和我們的App一樣,也是繼承自Activity

packages/apps/Launcher2/src/com/android/launcher2/Launcher.java

public final class Launcher extends Activity
        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
                   View.OnTouchListener {
                   }

Launcher實現了點選、長按等回撥介面,來接收使用者的輸入。既然是普通的App,那麼我們的開發經驗在這裡就仍然適用,比如,我們點選圖示的時候,是怎麼開啟的應用呢?如果讓你,你怎麼做這個功能呢?捕捉圖示點選事件,然後startActivity()傳送對應的Intent請求唄!是的,Launcher也是這麼做的,就是這麼easy!

那麼到底是處理的哪個物件的點選事件呢?既然Launcher是App,並且有介面,那麼肯定有佈局檔案呀,是的,我找到了佈局檔案launcher.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
    android:id="@+id/launcher">

    <com.android.launcher2.DragLayer
        android:id="@+id/drag_layer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

        <!-- Keep these behind the workspace so that they are not visible when
             we go into AllApps -->
        <include
            android:id="@+id/dock_divider"
            layout="@layout/workspace_divider"
            android:layout_marginBottom="@dimen/button_bar_height"
            android:layout_gravity="bottom" />

        <include
            android:id="@+id/paged_view_indicator"
            layout="@layout/scroll_indicator"
            android:layout_gravity="bottom"
            android:layout_marginBottom="@dimen/button_bar_height" />

        <!-- The workspace contains 5 screens of cells -->
        <com.android.launcher2.Workspace
            android:id="@+id/workspace"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingStart="@dimen/workspace_left_padding"
            android:paddingEnd="@dimen/workspace_right_padding"
            android:paddingTop="@dimen/workspace_top_padding"
            android:paddingBottom="@dimen/workspace_bottom_padding"
            launcher:defaultScreen="2"
            launcher:cellCountX="@integer/cell_count_x"
            launcher:cellCountY="@integer/cell_count_y"
            launcher:pageSpacing="@dimen/workspace_page_spacing"
            launcher:scrollIndicatorPaddingLeft="@dimen/workspace_divider_padding_left"
            launcher:scrollIndicatorPaddingRight="@dimen/workspace_divider_padding_right">

            <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
            <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
            <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
            <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
            <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
        </com.android.launcher2.Workspace>

    ...ignore some code...

    </com.android.launcher2.DragLayer>
</FrameLayout>

為了方便檢視,我刪除了很多程式碼,從上面這些我們應該可以看出一些東西來:Launcher大量使用<include/>標籤來實現介面的複用,而且定義了很多的自定義控制元件實現介面效果,dock_divider從佈局的引數宣告上可以猜出,是底部操作欄和上面圖示佈局的分割線,而paged_view_indicator則是頁面指示器,和App首次進入的引導頁下面的介面引導是一樣的道理。當然,我們最關心的是Workspace這個佈局,因為註釋裡面說在這裡麵包含了5個螢幕的單元格,想必你也猜到了,這個就是在首頁存放我們圖示的那五個介面(不同的ROM會做不同的DIY,數量不固定)。

接下來,我們應該開啟workspace_screen佈局,看看裡面有什麼東東。

workspace_screen.xml

<com.android.launcher2.CellLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingStart="@dimen/cell_layout_left_padding"
    android:paddingEnd="@dimen/cell_layout_right_padding"
    android:paddingTop="@dimen/cell_layout_top_padding"
    android:paddingBottom="@dimen/cell_layout_bottom_padding"
    android:hapticFeedbackEnabled="false"
    launcher:cellWidth="@dimen/workspace_cell_width"
    launcher:cellHeight="@dimen/workspace_cell_height"
    launcher:widthGap="@dimen/workspace_width_gap"
    launcher:heightGap="@dimen/workspace_height_gap"
    launcher:maxGap="@dimen/workspace_max_gap" />

裡面就一個CellLayout,也是一個自定義佈局,那麼我們就可以猜到了,既然可以存放圖示,那麼這個自定義的佈局很有可能是繼承自ViewGroup或者是其子類,實際上,CellLayout確實是繼承自ViewGroup。在CellLayout裡面,只放了一個子View,那就是ShortcutAndWidgetContainer。從名字也可以看出來,ShortcutAndWidgetContainer這個類就是用來存放快捷圖示Widget小部件的,那麼裡面放的是什麼物件呢?

在桌面上的圖示,使用的是BubbleTextView物件,這個物件在TextView的基礎之上,添加了一些特效,比如你長按移動圖示的時候,圖示位置會出現一個背景(不同版本的效果不同),所以我們找到BubbleTextView物件的點選事件,就可以找到Launcher如何開啟一個App了。

除了在桌面上有圖示之外,在程式列表中點選圖示,也可以開啟對應的程式。這裡的圖示使用的不是BubbleTextView物件,而是PagedViewIcon物件,我們如果找到它的點選事件,就也可以找到Launcher如何開啟一個App。

其實說這麼多,和今天的主題隔著十萬八千里,上面這些東西,你有興趣就看,沒興趣就直接跳過,不知道不影響這篇文章閱讀。

BubbleTextView的點選事件在哪裡呢?我來告訴你:在Launcher.onClick(View v)裡面。

   /**
     * Launches the intent referred by the clicked shortcut
     */
    public void onClick(View v) {
    
          ...ignore some code...
            
         Object tag = v.getTag();
        if (tag instanceof ShortcutInfo) {
            // Open shortcut
            final Intent intent = ((ShortcutInfo) tag).intent;
            int[] pos = new int[2];
            v.getLocationOnScreen(pos);
            intent.setSourceBounds(new Rect(pos[0], pos[1],
                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));
        //開始開啟Activity咯~
            boolean success = startActivitySafely(v, intent, tag);

            if (success && v instanceof BubbleTextView) {
                mWaitingForResume = (BubbleTextView) v;
                mWaitingForResume.setStayPressed(true);
            }
        } else if (tag instanceof FolderInfo) {
            //如果點選的是圖示資料夾,就開啟資料夾
            if (v instanceof FolderIcon) {
                FolderIcon fi = (FolderIcon) v;
                handleFolderClick(fi);
            }
        } else if (v == mAllAppsButton) {
        ...ignore some code...
        }
    }

從上面的程式碼我們可以看到,在桌面上點選快捷圖示的時候,會呼叫

startActivitySafely(v, intent, tag);

那麼從程式列表介面,點選圖示的時候會發生什麼呢?實際上,程式列表介面使用的是AppsCustomizePagedView物件,所以我在這個類裡面找到了onClick(View v)。

com.android.launcher2.AppsCustomizePagedView.java

/**
 * The Apps/Customize page that displays all the applications, widgets, and shortcuts.
 */
public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
        View.OnClickListener, View.OnKeyListener, DragSource,
        PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener,
        LauncherTransitionable {
            
     @Override
    public void onClick(View v) {
       
         ...ignore some code...
       
        if (v instanceof PagedViewIcon) {
            mLauncher.updateWallpaperVisibility(true);
            mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
        } else if (v instanceof PagedViewWidget) {
                 ...ignore some code..
         }
     }      
   }

可以看到,呼叫的是

mLauncher.startActivitySafely(v, appInfo.intent, appInfo);

和上面一樣!這叫什麼?這叫殊途同歸!

所以咱們現在又明白了一件事情:不管從哪裡點選圖示,呼叫的都是Launcher.startActivitySafely()。

  • 下面我們就可以一步步的來看一下Launcher.startActivitySafely()到底做了什麼事情。
 boolean startActivitySafely(View v, Intent intent, Object tag) {
        boolean success = false;
        try {
            success = startActivity(v, intent, tag);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
        }
        return success;
    }

呼叫了startActivity(v, intent, tag)

 boolean startActivity(View v, Intent intent, Object tag) {
 
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            boolean useLaunchAnimation = (v != null) &&
                    !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
            
            if (useLaunchAnimation) {
                if (user == null || user.equals(android.os.Process.myUserHandle())) {
                    startActivity(intent, opts.toBundle());
                } else {
                    launcherApps.startMainActivity(intent.getComponent(), user,
                            intent.getSourceBounds(),
                            opts.toBundle());
                }
            } else {
                if (user == null || user.equals(android.os.Process.myUserHandle())) {
                    startActivity(intent);
                } else {
                    launcherApps.startMainActivity(intent.getComponent(), user,
                            intent.getSourceBounds(), null);
                }
            }
            return true;
        } catch (SecurityException e) {
        ...
        }
        return false;
    }

這裡會呼叫Activity.startActivity(intent, opts.toBundle()),這個方法熟悉嗎?這就是我們經常用到的Activity.startActivity(Intent)的過載函式。而且由於設定了

 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

所以這個Activity會新增到一個新的Task棧中,而且,startActivity()呼叫的其實是startActivityForResult()這個方法。

@Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);
        }
    }

所以我們現在明確了,Launcher中開啟一個App,其實和我們在Activity中直接startActivity()基本一樣,都是呼叫了Activity.startActivityForResult()。

Instrumentation是什麼?和ActivityThread是什麼關係?

還記得前面說過的Instrumentation物件嗎?每個Activity都持有Instrumentation物件的一個引用,但是整個程序只會存在一個Instrumentation物件。當startActivityForResult()呼叫之後,實際上還是呼叫了mInstrumentation.execStartActivity()

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            ...ignore some code...    
        } else {
            if (options != null) {
                 //當現在的Activity有父Activity的時候會呼叫,但是在startActivityFromChild()內部實際還是呼叫的mInstrumentation.execStartActivity()
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
         ...ignore some code...    
    }

下面是mInstrumentation.execStartActivity()的實現

 public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
            ...ignore some code...
      try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess();
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
        }
        return null;
    }

所以當我們在程式中呼叫startActivity()的 時候,實際上呼叫的是Instrumentation的相關的方法。

Instrumentation意為“儀器”,我們先看一下這個類裡面包含哪些方法吧

我們可以看到,這個類裡面的方法大多數和Application和Activity有關,是的,這個類就是完成對Application和Activity初始化和生命週期的工具類。比如說,我單獨挑一個callActivityOnCreate()讓你看看

public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }

對activity.performCreate(icicle);這一行程式碼熟悉嗎?這一行裡面就呼叫了傳說中的Activity的入口函式onCreate(),不信?接著往下看

Activity.performCreate()

final void performCreate(Bundle icicle) {
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

沒騙你吧,onCreate在這裡呼叫了吧。但是有一件事情必須說清楚,那就是這個Instrumentation類這麼重要,為啥我在開發的過程中,沒有發現他的蹤跡呢?

是的,Instrumentation這個類很重要,對Activity生命週期方法的呼叫根本就離不開他,他可以說是一個大管家,但是,這個大管家比較害羞,是一個女的,管內不管外,是老闆娘~

那麼你可能要問了,老闆是誰呀?
老闆當然是大名鼎鼎的ActivityThread了!

ActivityThread你都沒聽說過?那你肯定聽說過傳說中的UI執行緒吧?是的,這就是UI執行緒。我們前面說過,App和AMS是通過Binder傳遞資訊的,那麼ActivityThread就是專門與AMS的外交工作的。

AMS說:“ActivityThread,你給我暫停一個Activity!”
ActivityThread就說:“沒問題!”然後轉身和Instrumentation說:“老婆,AMS讓暫停一個Activity,我這裡忙著呢,你快去幫我把這事辦了把~”
於是,Instrumentation就去把事兒搞定了。

所以說,AMS是董事會,負責指揮和排程的,ActivityThread是老闆,雖然說家裡的事自己說了算,但是需要聽從AMS的指揮,而Instrumentation則是老闆娘,負責家裡的大事小事,但是一般不拋頭露面,聽一家之主ActivityThread的安排。

如何理解AMS和ActivityThread之間的Binder通訊?

前面我們說到,在呼叫startActivity()的時候,實際上呼叫的是

mInstrumentation.execStartActivity()

但是到這裡還沒完呢!裡面又呼叫了下面的方法

ActivityManagerNative.getDefault()
                .startActivity

這裡的ActivityManagerNative.getDefault返回的就是ActivityManagerService的遠端介面,即ActivityManagerProxy。

怎麼知道的呢?往下看

public abstract class ActivityManagerNative extends Binder implements IActivityManager
{

//從類宣告上,我們可以看到ActivityManagerNative是Binder的一個子類,而且實現了IActivityManager介面
 static public IActivityManager getDefault() {
        return gDefault.get();
    }

 //通過單例模式獲取一個IActivityManager物件,這個物件通過asInterface(b)獲得
 private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };
}


//最終返回的還是一個ActivityManagerProxy物件
static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
    
     //這裡面的Binder型別的obj引數會作為ActivityManagerProxy的成員變數儲存為mRemote成員變數,負責進行IPC通訊
        return new ActivityManagerProxy(obj);
    }


}

再看ActivityManagerProxy.startActivity(),在這裡面做的事情就是IPC通訊,利用Binder物件,呼叫transact(),把所有需要的引數封裝成Parcel物件,向AMS傳送資料進行通訊。

 public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        if (profilerInfo != null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        if (options != null) {
            data.writeInt(1);
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

Binder本質上只是一種底層通訊方式,和具體服務沒有關係。為了提供具體服務,Server必須提供一套介面函式以便Client通過遠端訪問使用各種服務。這時通常採用Proxy設計模式:將介面函式定義在一個抽象類中,Server和Client都會以該抽象類為基類實現所有介面函式,所不同的是Server端是真正的功能實現,而Client端是對這些函式遠端呼叫請求的包裝。

為了更方便的說明客戶端和伺服器之間的Binder通訊,下面以ActivityManagerServices和他在客戶端的代理類ActivityManagerProxy為例。

ActivityManagerServices和ActivityManagerProxy都實現了同一個介面——IActivityManager。

class ActivityManagerProxy implements IActivityManager{}

public final class ActivityManagerService extends ActivityManagerNative{}

public abstract class ActivityManagerNative extends Binder implements IActivityManager{}

雖然都實現了同一個介面,但是代理物件ActivityManagerProxy並不會對這些方法進行真正地實現,ActivityManagerProxy只是通過這種方式對方法的引數進行打包(因為都實現了相同介面,所以可以保證同一個方法有相同的引數,即對要傳輸給伺服器的資料進行打包),真正實現的是ActivityManagerService。

但是這個地方並不是直接由客戶端傳遞給伺服器,而是通過Binder驅動進行中轉。其實我對Binder驅動並不熟悉,我們就把他當做一箇中轉站就OK,客戶端呼叫ActivityManagerProxy接口裡面的方法,把資料傳送給Binder驅動,然後Binder驅動就會把這些東西轉發給伺服器的ActivityManagerServices,由ActivityManagerServices去真正的實施具體的操作。

但是Binder只能傳遞資料,並不知道是要呼叫ActivityManagerServices的哪個方法,所以在資料中會新增方法的唯一標識碼,比如前面的startActivity()方法:

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
       
        ...ignore some code...
       
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

上面的START_ACTIVITY_TRANSACTION就是方法標示,data是要傳輸給Binder驅動的資料,reply則接受操作的返回值。

客戶端:ActivityManagerProxy =====>Binder驅動=====> ActivityManagerService:伺服器

而且由於繼承了同樣的公共介面類,ActivityManagerProxy提供了與ActivityManagerService一樣的函式原型,使使用者感覺不出Server是執行在本地還是遠端,從而可以更加方便的呼叫這些重要的系統服務。

但是!這裡Binder通訊是單方向的,即從ActivityManagerProxy指向ActivityManagerService的,如果AMS想要通知ActivityThread做一些事情,應該咋辦呢?

還是通過Binder通訊,不過是換了另外一對,換成了ApplicationThread和ApplicationThreadProxy。

客戶端:ApplicationThread <=====Binder驅動<===== ApplicationThreadProxy:伺服器

他們也都實現了相同的介面IApplicationThread

  private class ApplicationThread extends ApplicationThreadNative {}
  
  public abstract class ApplicationThreadNative extends Binder implements IApplicationThread{}
  
  class ApplicationThreadProxy implements IApplicationThread {}

剩下的就不必多說了吧,和前面一樣。

AMS接收到客戶端的請求之後,會如何開啟一個Activity?

OK,至此,點選桌面圖示呼叫startActivity(),終於把資料和要開啟Activity的請求傳送到了AMS了。說了這麼多,其實這些都在一瞬間完成了,下面咱們研究下AMS到底做了什麼。

注:前方有高能的方法呼叫鏈,如果你現在累了,請先喝杯咖啡或者是上趟廁所休息下

AMS收到startActivity的請求之後,會按照如下的方法鏈進行呼叫

呼叫startActivity()

@Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
            resultWho, requestCode, startFlags, profilerInfo, options,
            UserHandle.getCallingUserId());
    }

呼叫startActivityAsUser()

 @Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
            
            ...ignore some code...
            
        return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null, null, options, userId, null, null);
    }

在這裡又出現了一個新物件ActivityStackSupervisor,通過這個類可以實現對ActivityStack的部分操作。

  final int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
            Bundle options, int userId, IActivityContainer iContainer, TaskRecord inTask) {
            
            ...ignore some code...
            
              int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho,
                    requestCode, callingPid, calli