1. 程式人生 > >【凱子哥帶你學Framework】Activity啟動過程全解析

【凱子哥帶你學Framework】Activity啟動過程全解析

It’s right time to learn Android’s Framework !

前言

  • 一個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大量使用標籤來實現介面的複用,而且定義了很多的自定義控制元件實現介面效果,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, 
            
           

相關推薦

FrameworkActivity啟動過程解析

It’s right time to learn Android’s Framework ! 前言 一個App是怎麼啟動起來的? App的程式入口到底是哪裡? Launcher到底是什麼神奇的東西? 聽說還有個AMS的東西,它是做什麼的?

FrameworkActivity介面顯示解析

前幾天凱子哥寫的Framework層的解析文章《Activity啟動過程全解析》,反響還不錯,這說明“寫讓大家都能看懂的Framework解析文章”的思想是基本正確的。 我個人覺得,深入分析的文章必不可少,但是對於更多的Android開發者——即

Java執行緒和鎖

執行緒 在說執行緒之前我們瞭解下什麼是程序. 程序:一個正在執行的程式(獨立執行). 程序和執行緒之間的關係: 一個程序可以只有一個執行緒(單執行緒程式),一個執行緒中也可以有多個執行緒(多執行緒程式). 執行緒:一個執行緒,相當於cpu的執行路徑,一個獨

Java泛型

在說泛型之前先建三個類. 第一個、Person類 /* * 姓名 和 年齡 * 構造 set/get toString * 建立一個學生類(構造方法) 繼承人類 */ public class Person { private Stri

Java跟蹤行號流、裝飾者模式以及列印流

跟蹤行號流 跟蹤行號字元輸入流:LineNumberReader 這個流有什麼特點呢? 沒什麼特別的特點,就是可以獲取行號. public void fun() throws FileNotFound

Java抽獎小遊戲

使用者資訊類: public class User { static String username; // 註冊時存入的使用者名稱 static String password; //

Java執行緒中斷

執行緒中斷 (一)stop方法 在早期,執行緒中又一個stop方法,可以直接終止執行緒.但是現在已經不用了,而且api上也已經在方法下標明已過時.這時因為stop方法一旦呼叫,不管執行緒是什麼狀態的都直接會被終 止,假如正在執行執行緒,run方法中的程式

Java static的用法

一、封裝 在說static關鍵詞的用法之前我們先說說Java面向物件的三大特徵之一的封裝. 什麼是封裝? 講類中的屬性或方法對外界隱藏,然後開放公共的訪問方式. 那麼怎麼隱藏呢? 之前我們寫mai

Java集合與迭代器

Java中的集合 為什麼會有集合? 因為陣列有弊端,所有才有了集合. 陣列的特點是什麼? 首先,陣列中只能新增相同型別的元素(基本資料型別和引用資料型別都能儲存),而且陣列的長度一旦確定就不能該改變, 如果要新增超出陣列長度個數的元素,就只能再宣告一個新的

Java之JDBC

JDBC 什麼是JDBC? JDBC(Java Data Base Connectivity,java資料庫連線)是一種用於執行SQL語句 的JavaAPI,可以為多種關係資料庫提供統一訪問,它由一組用Java語言編寫的類和介面組成.JDBC提供了一種基準

python-[第一章-初識Python]

license div 高級 今天 做什麽 挖掘 運維 自然語言 什麽 Python是一種解釋型、面向對象、動態數據類型的高級程序設計語言。 Python由Guido van Rossum於1989年底發明,第一個公開發行版發行於1991年。 像Perl語言一樣, Py

昊昊android-解決/data/dalvik-cache佔用記憶體的問題

較新款android手機應該不存在此類問題。一般都是記憶體容量較小的老android手機會有這個問題。 安裝軟體的空間非常小,可以通過app2sd把軟體移到sd卡上(sd卡上分一個ext2分割槽)。有時候做完app2sd後可能依舊發現空間沒有變大多少。 觀察/da

昊昊VMWare+Mountain Lion

直接黑蘋果看起來挺美好,吃起來挺困難,所以,如果你只是準備用Mac來做一些碼農的活,而不是很追求效能的話,或許可以考慮一下虛擬機器裝Mac。 言歸正傳,折騰了許久,終於在虛擬機器下裝好了山獅。 第一步有傳言某些CPU不支援虛擬化,不過我是沒有遇到什麼問題,所以沒有

牛客程式設計C++方向專案練習第1期

//普通構造 MyString::MyString(const char *str){ if(str == NULL){ m_data = new char[1]; /

昊昊基本資料結構(下)

連結串列 前面大家用隊、棧的時候用的都是陣列。陣列挺好用的,不過蛋疼的是,數組裡面要增、刪資料就蛋疼了。不過咱們有連結串列,下面我來醜陋地給大家畫個連結串列~ 哈哈,說了畫給大家,就果斷要親手畫,果然很醜陋的說 O_o 我來簡單給大家先說一下連結串列的每個節點都

昊昊寬搜(BFS)

**************************轉載請註明出處!******************************          很久很久以前,我講了深搜(這到底是有多久 - -|||)。對應的當然就得有寬搜。搜尋的概念上次深搜已經講過了,可以去“複習

昊昊演算法概述

什麼是演算法         雖然這篇題目叫概述,不過本菜只是不知道該叫什麼好,我這種小白腫麼敢概述演算法是不。就是總體上說說我對演算法的理解吧 O_o         演算法可能有些初學童鞋接觸過。有些地方高中教材裡會涉及到(江蘇是有的)。《演算法導論》給出了一個定

從“零”玩轉Html5 + 跨平臺開發學習筆記

最近一段時間都沒有寫部落格,一是因為有新的專案要做,而是最近在學習有關H5移動開發相關方面的內容,以下是看了“【江哥帶你從“零”玩轉Html5 + 跨平臺開發】”總結的學習筆記,目前只是更新到了H5+CSS3,相關視訊網站地址:http://bbs.520it.com/fo

玩轉MySQL索引篇(一)索引揭祕,看他是如何讓的查詢效能指數提升的

  場景復現,一個索引提高600倍查詢速度? 首先準備一張books表 create table books( id int not null primary key auto_increment, name varchar(255) not null, author va

為什麼MySQL要用B+樹?聊聊B+樹與硬碟的前世今生玩轉MySQL 索引篇(二)

為什麼MySQL要用B+樹?聊聊B+樹與硬碟的前世今生   在上一節,我們聊到資料庫為了讓我們的查詢加速,通過索引方式對資料進行冗餘並排序,這樣我們在使用時就可以在排好序的資料裡進行快速的二分查詢,使得查詢效率指數提升。但是我在結尾同樣提到一個問題,就是記憶體大小一般是很有限的,不可能把一個表所有的