美文网首页
Activity 启动源码分析(一)——从Launcher到AM

Activity 启动源码分析(一)——从Launcher到AM

作者: Geeks_Liu | 来源:发表于2016-11-13 01:35 被阅读1255次

    在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的布局:

    ![ ![](https://img.haomeiwen.com/i2179030/b6d4b1c77e79180e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](https://img.haomeiwen.com/i2179030/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启动的第一个阶段就结束了。

    相关文章

      网友评论

          本文标题:Activity 启动源码分析(一)——从Launcher到AM

          本文链接:https://www.haomeiwen.com/subject/aztcpttx.html