Activity 啟動源碼分析(一)——從Launcher到AMS

在Android開發中,我們經常會遇到界面的跳轉和回退,在開發中與之聯系比較緊密的概念是Task(任務)和Back Stack(回退棧)。我們在本文中所要講的Activity的啟動模式會影響Task和Back Stack的狀態,進而影響用戶體驗。
??首先我們對幾個概念做以說明:

一.?Application,Task和Process

1.1Application

Appliction可以翻譯為“應用”或者"應用程序",Androdi是一個在應用層組件化程度很高的系統,android的基礎就是四大組件。任何一個Android Application基本上都是由一個個基礎的組件組成,當我們寫完了多個組件,并在manifest文件中注冊了這些組件之后,這些捆綁在一起的組件就成了一個處理特定需求的Application,并且以“.apk”作為后綴名存在于文件系統。Android平臺下默認的應用程序,如:Email,Calendar,Camera等都是一個個獨立的App。Application和組件的關系可以在mainfest文件中清晰的表示出來:

<?xml version="1.0" encoding="utf-8"?>  
<manifest android:versionCode="1"  
        android:versionName="1"  
        xmlns:android="http://schemas.android.com/apk/res/android"  
        package="com.example.android.myapp">  
  
    <application android:label="@string/app_name">  
        <activity android:name=".MyActivity" android:label="@string/app_nam">  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
    <receiver android:name=".MyReceiver"/>  
    <provider android:name=".MyProvider"/>  
    <service android:name=".MyService"/>  
    </application>  
</manifest> 

由此可見,Application是由四大組件組成:Activity,Service,Content Provider和Broadcast Receiver,其中Activity是實現應用的主體。在安裝APP的時候,系統會讀取mainfest的信息,將所有組件解析出來,以便在運行的時候對組件進行實例化和調度。
????Activity是Applications的主要組成部分,我們呢可以將Application理解為一個抽象標簽,他將系統內的一部分Activityes聯系在一起,協同完成用戶的特定需求。安裝Application的過程也可以簡單的理解為將其所包含的一系列Acticities導入到當前系統中,如果系統中已經存在了相同的Activity,那么將會自動的將其關聯,而不會重復的安裝Activities,避免資源的浪費;同理卸載的過程也會檢查當前所關聯的Activies是否被其他Application標簽所關聯,如果僅僅是提供當前的Application使用,那么他將被徹底的刪除,相反不做任何操作。
????用戶與Application的交互行為大部分是通過GUI來完成,在Android平臺可以有兩種方式定義GUI,一種是在XML文件中靜態的設置GUI元素,另一種是在JAVA代碼中動態的設置。這兩種方式都是Activity作為驅動和響應用戶交互時間愛你的主體。當Application啟動之后,至少需要一個包含GUI信息的Activity實例被創建(manifest中帶 category android:name="android.intent.category.LAUNCHER"標簽的那個Activity)

1.2Task與Back Stack

Task是程序在運行的過程中,只針對Activity的概念。說白了,Task是一組相互關聯的activity的集合,他是FrameWork層的一個概念,控制界面的跳轉和返回。這個Task存在于一個叫Back Stack的數據結構中,也就是說,FrameWork是以的形式管理用戶開啟的Activity。這個棧的基本行為是,當用戶在多個Activity之間跳轉的時候,執行壓棧操作,當用戶按返回鍵時,執行出棧操作。當一個Activity啟動了另外一個Activity的時候,新的Activity就會被放置到返回棧的棧頂并將獲得焦點。前一個Activity仍然保留在返回棧當中,但會處于停止狀態。當用戶按下Back鍵的時候,棧中最頂端的Activity會被移除掉,然后前一個Activity則會得重新回到最頂端的位置,重復下去,直到任務棧為空,系統就會回收這個任務棧。返回棧(Back Stack)中的Activity的順序永遠都不會發生改變,我們只能向棧頂添加Activity,或者將棧頂的Activity移除掉。因此,返回棧(Back Stack)是一個典型的后進先出(last in, first out)的數據結構

Task是可以跨應用的,這正是Task存在的一個重要原因。有的Activity,雖然不再同一個app中,但是為了保持用戶操作的連貫性,可以把他們放在同一個Task中。比如我們在app的Activity A中執行一個添加圖片的操作,此時會調用系統相冊的Activity B,這兩個Activity是在不同的app中的,但是被系統放在同一個任務(Task)中,前面我們已經說過這個Task所在的Back Stack是一個先進先出的任務棧,其中的Activity順序不會改變,因此,此時我們執行完添加圖片的操作之后,點Back返回(一般在程序中是finish,不過道理是一樣的)時,Activty B銷毀,直接退回到我們app中的Activity A,這樣就保證了用戶體驗的連貫性。
????關于更多Task和Back Stack的知識點可以參考谷歌原文,https://developer.android.com/guide/components/tasks-and-back-stack.html或者郭霖大神的這篇譯文:http://blog.csdn.net/guolin_blog/article/details/41087993

1.3process

process一般翻譯成進程,進程是操作系統內核中的一個概念,表示直接受內核調度的執行單位。每個App在啟動之前必須先創建一個進程,該進程是由Zygote?fork出來的,該進程具有獨立的資源空間,用于承載App上運行的各種Activity/Service等組件。在默認情況下,一個應用程序中的所有組件都運行在同一個進程中。除非我們在manifest中用process屬性指定組件所運行的進程的名字:

<activity android:name=".MyActivity" 
        android:label="@string/app_nam"
        android:process=":remote">
</activity>
1.4Android系統應用框架中的Activity,Task,process

在Android系統的應用框架中,ActivityManagerService(AMS)負責啟動Activity,在整個啟動的過程中,ActivityManagerService用于管理Activity的生命周期。這里我們可以先不管AMS是什么,后面會有詳細的介紹。
????AMS提供了一個ArrayList mHistory來管理所有的activity:

首先我們來了解一下幾個概念:

  • [ ] ActivityStack
    ????Activity在AMS的棧管理,用來記錄已經啟動的Activity先后順序,狀態信息等。通過ActivityStack決定是否需要啟動新的進程。
  • [ ] ActivityRecord
    ????ActivityStack的管理對象,每個Activity在AMS對應的一個ActiivtyRecord,來記錄Activity的狀態及其他管理信息。
  • [ ] TaskRecord
    ????AMS抽象出來的一個“任務”的概念,是記錄ActivityRecord的棧,一個“Task”包含若干個ActivityRecord。AMS用TaskRecord確保Activity的q啟動和退出順序。
  • [ ] ProcessRecord
    ????一個APK文件運行時會對應一個進程,ProcessRecord正是記錄一個進程相關的信息。

二.StartActivity流程

在Android系統中,應用程序是由Activity組成,因此,應用程序的啟動實際上是應用程序中默認的Activity啟動過程。啟動Activity有兩種情景:
????第一,在android屏幕上點擊應用程序圖標啟動默認的Activity(就是在manifest中設為Launcher的那個Activity)。這種啟動方式的特點就是會啟動一個新的進程來加載相應的Activity。
????第二,應用程序內部啟動非默認Activity的過程。這種非默認的Activity一般是在原來的進程和任務中啟動的。在Android的Activity管理機制中,當退出Actiivty的時候,在某些情況下并沒有立刻把Aactivity殺死,而是將其暫時保存起來,當第二次啟動他的時候,就不需要再創建該Activity的實例直接恢復即可(比如在應用中選一張相片,跳到相冊界面中后又返回應用中,那么在這個過程中應用本身的那個Actiivty并沒有被銷毀)。

2.1在新的進程中啟動Activity
1.Launcher是什么

在新的進程中啟動Actiivity,也就是由Launcher啟動Activity,當我們點擊桌面的圖標的時候,App就由Launcher啟動了。我們在Android的源碼中找到Launcher類(筆者用源碼版本的是Android6.0),源碼目錄:
packages/apps/Launcher2/src/com/android/launcher2/Launcher.java

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

其實從目錄文件中我們可以看出:他位于packages/apps這個目錄下,也就是說他本質上也是一個APP,我們看他的第一句,發現這個Launcher和普通的App一樣,也是繼承自Activity。根據開發經驗我們得知既然繼承自Activity那么應唉就有布局文件,我么用Android Studio的ctrl+F搜素setContentView方法,在源碼的390行真的找到了這個方法:setContentView(R.layout.launcher);我們來看看這個布局文件(packages/apps/Launcher2/res/layout-land/launcher.xml):

2.Launcher.xml解析
<!-- Full screen view projects under the status bar and contains the background -->
<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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/workspace_bg">

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

        <!-- The workspace contains 5 screens of cells -->
        <!-- Workspace即存放手機圖標的桌面,默認系統是包含可翻轉5頁,其中launcher:defaultScreen="2"
            這句代表默認有兩個菜單界面,一個存放所有已安裝的應用程序圖標,另一個存放小部件-->
        <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/qsb_bar_height"
            launcher:scrollIndicatorPaddingRight="@dimen/button_bar_height">

            <!-- Workspace總共可翻轉5個頁面,一個 workspace_screen定義一個頁面布局-->
            <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>

        <!-- dock_divider為左桌面分隔線,將hotseat和Workspace分隔,注意筆者這里的源碼是衡屏時的源碼,
            所以出現了左分隔線和右分隔線,如果是豎屏時的源碼(比如面三張說明圖)應當是上下分隔線-->
        <include
            android:id="@+id/qsb_divider"
            layout="@layout/workspace_divider"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginStart="@dimen/qsb_bar_height"
            android:layout_gravity="start" />

        <!-- dock_divider為右喲桌面分隔線,將hotseat和Workspace分隔 -->
        <include
            android:id="@+id/dock_divider"
            layout="@layout/workspace_divider"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginEnd="@dimen/button_bar_height"
            android:layout_gravity="end" />

        <!-- 桌面分隔線的上指示器,Workspace翻頁的時候顯示 -->
        <include
            android:id="@+id/paged_view_indicator"
            layout="@layout/scroll_indicator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom" />

        <!-- hotseat為桌面分隔線下的界面, 不隨Workspace翻頁操作而移動 -->
        <include layout="@layout/hotseat"
            android:id="@+id/hotseat"
            android:layout_width="@dimen/button_bar_height_plus_padding"
            android:layout_height="match_parent"
            android:layout_gravity="end" />

        <!-- qsb_bar布局包含桌面上的可搜索框 以及長按桌面上圖標時顯示刪除和應用信息的操作框-->
        <include
            android:id="@+id/qsb_bar"
            layout="@layout/qsb_bar" />

        <!-- The Workspace cling must appear under the AppsCustomizePagedView below to ensure
             that it is still visible during the transition to AllApps and doesn't overlay on
             top of that view. -->
        <!-- 手機剛開機,或者對launcher應用清空數據第一次進入到workspace時彈出的操作介紹界面 --> 
        <include layout="@layout/workspace_cling"
            android:id="@+id/workspace_cling"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone" />

        <!-- 手機剛開機,或者對launcher應用清空數據,第一次打開將workspace上兩個以上的圖標拖到一起形成 的文件夾時彈出的操作界面-->
        <include layout="@layout/folder_cling"
            android:id="@+id/folder_cling"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone" />

        <com.android.launcher2.DrawableStateProxyView
            android:id="@+id/voice_button_proxy"
            android:layout_width="@dimen/qsb_bar_height"
            android:layout_height="@dimen/app_icon_size"
            android:layout_gravity="top|start"
            android:layout_marginTop="64dp"
            android:clickable="true"
            android:onClick="onClickVoiceButton"
            android:importantForAccessibility="no"
            launcher:sourceViewId="@+id/voice_button" />

        <!-- 點擊hotseat中心圖標進入的界面,該界面顯示所有應用和小部件 -->
        <include layout="@layout/apps_customize_pane"
            android:id="@+id/apps_customize_pane"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible" />
    </com.android.launcher2.DragLayer>
</FrameLayout>

我們通過這幾張網上的圖片來說明Launcher的布局:

![ ![](http://upload-images.jianshu.io/upload_images/2179030-b6d4b1c77e79180e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](http://upload-images.jianshu.io/upload_images/2179030-1d71a1fe73210bd9.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
圖1:Launcher的桌面布局(從上到下為搜索框qsb_bar、Workspace、分割線dock_divider、hotseat)。
圖2:菜單界面(apps_customize_pane)之一的顯示所有已安裝的應用程序;
圖3:菜單界面(apps_customize_pane)之一的顯示所有已創建的widget(小部件)
注:菜單界面整體是一個TabHost,由兩個子Tab組成;一個就是顯示圖2界面的子Tab,另一個就是顯示圖3界面的子Tab

下面我們重點來看xml文件中的這段代碼:com.android.launcher2.Workspace:

public class Workspace extends SmoothPagedView
        implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
        DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener {
public abstract class SmoothPagedView extends PagedView {
public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {

該子視圖為自定義類Workspace,Workspace繼承自SmoothPagedView,SmoothPagedView繼承自PagedView,PagedView繼承自ViewGroup;所以Workspace的終極父類也是ViewGroup;即該子視圖為ViewGroup類型的自定義容器視圖,也就是用來存放APP圖標的桌面;
????在Workspace視圖中又包含了5個id分別為cell1、cell2、cell3、cell4、cell5的子視圖,也就是多個存放圖片的自頁面,類似于ViewPager中的Fragment。它們對應的布局均為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" />

可以看到,這里workspace_screen的布局文件就是一個cellLayout(上面有說過這一點),既然CellLayout可以用來從房圖標,那么我們可以猜想他是繼承子ViewGroup或者其子類的,實際上cellLayout確實繼承自ViewGroup,我們呢點進去cellLayout的源碼,由于我們在這里不會分析該類的源碼,所以我們先不貼出。我們要關注的是其中的元素:ShortcutAndWidgetContainerBubbleTextView
????ShortcutAndWidgetContainer繼承自ViewGroup類,他是cellLayout中唯一的自View。其實看名字我們也能猜得出,他是一個用來放置快捷圖標Widget小部件的View。
????而我們重點要看的就是BubbleTextView這個東西,我們繼續上源碼:

/**
 * TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
 * because we want to make the bubble taller than the text and TextView's clip is
 * too aggressive.
 */
public class BubbleTextView extends TextView {

    ......
    public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) {
        Bitmap b = info.getIcon(iconCache);

        setCompoundDrawablesWithIntrinsicBounds(null,
                new FastBitmapDrawable(b),
                null, null);
        setText(info.title);
        if (info.contentDescription != null) {
            setContentDescription(info.contentDescription);
        }
        setTag(info);
    }

    ......

我們首先可以看到他是繼承了TextView的一個類,也就是說他是一個強化版的TextView,而他有TextView的基本屬性——添加文本。我們再看他的注釋:“TextView是在文本后面繪制一個氣泡。我們不能使用LineBackgroundSpan,因為我們想讓氣泡比文本更高,TextView的剪輯太激進了。”也就是說,TextView=文本+氣泡View,這個氣泡View是在文本之后的,我們現在需要把這個氣泡View移動到文本上面去。
????然后我們又看到applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache)這個方法中,可以看到他獲得了icon,又進行了一些設置(setCompoundDrawablesWithIntrinsicBounds(null, new FastBitmapDrawable(b), null, null);)結合上面的注釋,我們猜也猜的出來,這個BubbleTextView就是我們桌面上的一個個APP的圖標。
????OK,到這一步之后,我們已經找到了桌面的圖標了,那么下一步就是點擊啟動他了。那么BubbleTextView的點擊事件在哪里呢?我們注意到上面applyFromShortcutInfo這個方法,在Android Studio中ctrl+左鍵可以看到有兩個調用他的類:launcher和workspace,我們重新回到Launcher類(packages/apps/Launcher2/src/com/android/launcher2/Launcher.java):

    /**
     * Creates a view representing a shortcut inflated from the specified resource.
     *
     * @param layoutResId The id of the XML layout used to create the shortcut.
     * @param parent The group the shortcut belongs to.
     * @param info The data structure describing the shortcut.
     *
     * @return A View inflated from layoutResId.
     */
    View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
        BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
        favorite.applyFromShortcutInfo(info, mIconCache);   //給BubbleTextView設置相應App的icon
        favorite.setOnClickListener(this);  //給BubbleTextView設置點擊事件
        return favorite;
    }
    /**
     * Launches the intent referred by the clicked shortcut.
     *
     * @param v The view representing the clicked shortcut.
     */
    public void onClick(View v) {
        // Make sure that rogue clicks don't get through while allapps is launching, or after the
        // view has detached (it's possible for this to happen if the view is removed mid touch).
        if (v.getWindowToken() == null) {
            return;
        }

        if (!mWorkspace.isFinishedSwitchingState()) {
            return;
        }

        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()));

            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) {
            if (isAllAppsVisible()) {
                showWorkspace(true);
            } else {
                onClickAllAppsButton(v);
            }
        }
    }

可以看到在Launcher中我們終于實現了BubbleTextView的onClick方法,然后在在這個方法中調用了startActivitySafely(v, intent, tag)方法,我們呢繼續看源碼:

    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 {
            // Only launch using the new animation if the shortcut has not opted out (this is a
            // private contract between launcher and may be ignored in the future).
            boolean useLaunchAnimation = (v != null) &&
                    !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
            UserHandle user = (UserHandle) intent.getParcelableExtra(ApplicationInfo.EXTRA_PROFILE);
            LauncherApps launcherApps = (LauncherApps)
                    this.getSystemService(Context.LAUNCHER_APPS_SERVICE);
            if (useLaunchAnimation) {
                ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
                        v.getMeasuredWidth(), v.getMeasuredHeight());
                if (user == null || user.equals(android.os.Process.myUserHandle())) {
                    // Could be launching some bookkeeping activity
                    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) {
            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Launcher does not have the permission to launch " + intent +
                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
                    "or use the exported attribute for this activity. "
                    + "tag="+ tag + " intent=" + intent, e);
        }
        return false;
    }

調用了startActivity(intent, opts.toBundle());這句就是我們平常在開發中調用的Activity.startActivity(Intent)重載方法。并且由于設置了intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);所以這個Actiivity就會添加到一個新的Task棧中。
????我們接著看源碼(frameworks/base/core/java/android/app/Activity.java):

    @Override
    public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }
    
    @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);
        }
    }

調用了startActivityForResult():

    public void startActivityForResult(Intent intent, int requestCode) {
        startActivityForResult(intent, requestCode, null);
    }
    
    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());
            }
            if (requestCode >= 0) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

注意這里出現了一個新的東西:Instrumentation,每個Activity都持有一個Instrumentation對象的引用,但是整個進程只會存在一個Instrumentation對象。當StartActivityForResult()調用之后,實際上還是調用了Instrumentation.execStartActivity()。
????Instrumentation意為“儀器”的意思,這個類里邊的方法大多數和Application和Activity有關。準確的說,這個類就是完成對Application和Activity的初始化和生命周期的調控。
????在上面這段代碼中,我們掉用了mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);方法,這里的第二個參數mMainThread.getApplicationThread()就是ApplicationThread類型;mToken就是IBinder類型的。我們接著看源碼(frameworks/base/core/java/android/app/Instrumentation.java):

    public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, String target,
        Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess();
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target, requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

這里我們需要注意到這兩句代碼:

int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target, requestCode, 0, null, options);
            checkStartActivityResult(result, intent);

我們呢看看這個ActivityManagerNative.getDefault()的源碼(frameworks/base/core/java/android/app/ActivityManagerNative.java):

    /**
     * Retrieve the system's default/global activity manager.
     */
    static public IActivityManager getDefault() {
        return gDefault.get();
    }

gDefault源碼:

    //通過單例模式獲取一個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;
        }
    };

asInterface(b)源碼,其中參數b為IBinder類型的數據:

    /**
     * Cast a Binder object into an activity manager interface, generating
     * a proxy if needed.
     */
     //最終返回的還是一個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);
    }

繞了一圈,到這里我們可以看到,execStartActivity()中的ActivityManagerNative.getDefault()返回的實際上就是通過單例模式返回的一個ActivityManagerProxy的對象,而在這個過程中,我們傳遞了一個IBinder類型的對象:new ActivityManagerProxy(obj),這個IBinder數據是通過IBinder b = ServiceManager.getService("activity");產生的,也就是說該變量是一個與activity相關的信息,具體是什么信息我們暫時不做探討。
????我們點進去ActivityManagerProxy這個類看下(frameworks/base/core/java/android/app/ActivityManagerNative.java):

class ActivityManagerProxy implements IActivityManager
{
    public ActivityManagerProxy(IBinder remote)
    {
        mRemote = remote;
    }

    public IBinder asBinder()
    {
        return mRemote;
    }

    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;
    }
    ......
}

這里的的startActivity()方法就是前面我們說道重要的兩句代碼中的int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target, requestCode, 0, null, options);方法。我們首先看一下這個方法中的參數:

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags,
            ProfilerInfo profilerInfo, Bundle options) throws RemoteException;
            
caller: 當前應用的ApplicationThread對象mAppThread;
callingPackage: 調用當前ContextImpl.getBasePackageName(),獲取當前Activity所在包名;
intent: 這便是啟動Activity時,傳遞過來的參數;
resolvedType: 調用intent.resolveTypeIfNeeded而獲取;
resultTo: 來自于當前Activity.mToken
resultWho: 來自于當前Activity.mEmbeddedID
requestCode = -1;
startFlags = 0;
profilerInfo = null;
options = null;

這個方法中要做的事情就是IPC通信,利用Binder對象,調用mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);方法,把所需要的參數封裝成Parcel對象,向AMS通信。這里我們打包成Parcel對象的有兩個重要的信息:caller——當前應用的的ApplicationThread對象,callingPackage——調用當前ContextImpl.getBasePackageName(),獲取當前Activity所在包名。
????這里不得不多說一點,這個ActivityManagerProxy是干什么的?實際上ActivityManagerProxy就是ActivityManagerService在客戶端的代理。嗯,可能到這里有些同學就有些糊涂了——客戶端是什么鬼?嗯,我們一定聽說過C/S框架吧?實際上,服務器客戶端的概念不僅僅存在于Web開發中,在Android的框架設計中使用的也是這種模式。服務其端指的是所有App共用的系統服務,比如我們在這里提到的ActivityManagerService,WindowManagerService以及PackageManagerService等等,這些基礎的系統服務是被所有的APP公用的,當某個App想實現某個操作的時候,就告訴這些系統服務,然后由這些系統服務調用對應APP的具體方法來實現,而這些App或者更具體的Activity就是對應的概念上的客戶端。再具體一點,在我們本篇文章的分析中,AMS就是服務端,而AactiviyThread以及具體的Aactiviy就是客戶端。
????我們接著看源碼,在ActivityManagerNative.getDefault().startActivity()中,我們需要注意到最后他調用了mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);方法,這里mRemote是IBinder類的對象,而IBinder類是一個接口,其中定義了該方法:

    /**
     * Perform a generic operation with the object.
     *
     * @param code The action to perform.  This should
     * be a number between {@link #FIRST_CALL_TRANSACTION}
     * {@link #LAST_CALL_TRANSACTION}.
     * @param data Marshalled data to send to the target.  Must not be null.
     * If you are not sending any data, you must create an empty Parcel
     * that is given here.
     * @param reply Marshalled data to be received from the target.  May be
     * null if you are not interested in the return value.
     * @param flags Additional operation flags.  Either 0 for a normal
     * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
     */
    public boolean transact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException;

而在Binder類中實現了IBinder接口,并通過下列方法傳遞了參數:

    /**
     * Default implementation rewinds the parcels and calls onTransact.  On
     * the remote side, transact calls into the binder to do the IPC.
     */
    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }
    /**
     * Default implementation is a stub that returns false.  You will want
     * to override this to do the appropriate unmarshalling of transactions.
     *
     * <p>If you want to call this, call transact().
     */
    protected boolean onTransact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (code == INTERFACE_TRANSACTION) {
            reply.writeString(getInterfaceDescriptor());
            return true;
        } else if (code == DUMP_TRANSACTION) {
            ParcelFileDescriptor fd = data.readFileDescriptor();
            String[] args = data.readStringArray();
            if (fd != null) {
                try {
                    dump(fd.getFileDescriptor(), args);
                } finally {
                    try {
                        fd.close();
                    } catch (IOException e) {
                        // swallowed, not propagated back to the caller
                    }
                }
            }
            // Write the StrictMode header.
            if (reply != null) {
                reply.writeNoException();
            } else {
                StrictMode.clearGatheredViolations();
            }
            return true;
        }
        return false;
    }

可以看到,int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target, requestCode, 0, null, options);方法中封裝成Parcel對象的一系列參數最終出傳到了Binder類中的boolean onTransact(int code, Parcel data, Parcel reply,
int flags)方法中,為什么要說這些呢?因為接下來,我們的startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target, requestCode, 0, null, options);方法所在的ActivityManagerNative類是繼承自Binder的:

public abstract class ActivityManagerNative extends Binder implements IActivityManager{

而在這個類中我們找到了如下方法:

    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        case START_ACTIVITY_TRANSACTION:
        {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            String callingPackage = data.readString();
            Intent intent = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            IBinder resultTo = data.readStrongBinder();
            String resultWho = data.readString();
            int requestCode = data.readInt();
            int startFlags = data.readInt();
            ProfilerInfo profilerInfo = data.readInt() != 0
                    ? ProfilerInfo.CREATOR.createFromParcel(data) : null;
            Bundle options = data.readInt() != 0
                    ? Bundle.CREATOR.createFromParcel(data) : null;
            int result = startActivity(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
            reply.writeNoException();
            reply.writeInt(result);
            return true;
        }

        case START_ACTIVITY_AS_USER_TRANSACTION:
        {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            String callingPackage = data.readString();
            Intent intent = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            IBinder resultTo = data.readStrongBinder();
            String resultWho = data.readString();
            int requestCode = data.readInt();
            int startFlags = data.readInt();
            ProfilerInfo profilerInfo = data.readInt() != 0
                    ? ProfilerInfo.CREATOR.createFromParcel(data) : null;
            Bundle options = data.readInt() != 0
                    ? Bundle.CREATOR.createFromParcel(data) : null;
            int userId = data.readInt();
            int result = startActivityAsUser(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags, profilerInfo, options, userId);
            reply.writeNoException();
            reply.writeInt(result);
            return true;
        }
        ......

        //省略的代碼是多個case,針對不同的標志符做出一系列處理

可以看到搞了半天,參數又回調了這個類中,而在上面的方法中不同的case分別調用了:

case START_ACTIVITY_TRANSACTION:
{
    int result = startActivity(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
}
case START_ACTIVITY_AS_USER_TRANSACTION:
{
    int result = startActivityAsUser(app, callingPackage, intent, resolvedType,
                        resultTo, resultWho, requestCode, startFlags, profilerInfo, options, userId);
}
case START_ACTIVITY_AS_CALLER_TRANSACTION:
{
    int result = startActivityAsCaller(app, callingPackage, intent, resolvedType,
                        resultTo, resultWho, requestCode, startFlags, profilerInfo, options,
                        ignoreTargetSecurity, userId);
}
case START_ACTIVITY_AND_WAIT_TRANSACTION:
{
    WaitResult result = startActivityAndWait(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags, profilerInfo, options, userId);
}
......

等等,這些各種各樣的startActivity實際上都是IActivityManager接口中封裝的方法,而真正實現這些方法的是在ActivityManagerService中:

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
public abstract class ActivityManagerNative extends Binder implements IActivityManager{

到此為止,結合上面我們所說的客戶—服務(C/S)端的理念,我們可以看到,由Launcher發起的啟動App默認Activity的請求已經成功的發送到了我們的服務端ActivityManagerService(AMS)中。到這里,ctivity啟動的第一個階段就結束了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容