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阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容