第11章 应用程序Activity的启动和调度

Activity[1]是应用程序的四大组件之一,为用户提供可以交互的界面。本章将分析应用程序Activity启动和调度机制的实现。Activity的启动和调度是糅合在一起的,这里以启动应用程序Activity为主线,分析其调度过程。

启动应用程序Activity的方法主要有以下五种:

在Home桌面点击应用程序的图标启动应用程序。其实质是启动应用程序的主Activity。

在应用程序中调用startActivity(intent,……)或者startActivityForResult(intent, requestCode,……)启动另外一个Activity。这个被启动的Activity可以位于当前应用程序,也可以位于其他应用程序。

按Back键,结束当前Activity,返回上一个Activity。

长按Home键,显示最近运行Task列表,选择启动其中一个Task的当前Activity。

通过adb shell命令登录手机,执行am start命令启动指定的Activity。

这些方法都是通过Binder机制的Client端,调用Server端的ActivityManagerService的startActivityXXX系列方法,最终启动指定的Activity。

本书第9章分析了APK的安装过程,为了承接这部分内容,本章将以第一种方法为例详细分析应用程序主Activity的启动和调度过程,其他方法只在Client端有所区别,在Server端的机制是一样的。

11.1 启动应用程序Activity时在Client端的执行流程

本节以从Home桌面启动应用程序主Activity为例,分析启动应用程序Activity时在Client端的执行流程。Android默认Home桌面的源码位于packages/apps/Launcher2,其进程名为com.android.launcher,这里简称为Launcher。

Launcher启动时,会通过PackageManagerService查询所有在AndroidManifest.xml中配置了以下Intent过滤条件的Activity组件:


<activity……>

<intent-filter>

<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>

……

</intent-filter>

</activity>


设置了上述Intent过滤条件的Activity便是应用程序的主Activity。Launcher会将该组件信息与应用程序图标关联,这样在桌面点击图标就可以启动应用程序进而启动其主Activity。

可以通过Eclipse调试应用程序进程,迅速定位点击应用程序图标的调用栈,如图11-1所示。

第11章 应用程序Activity的启动和调度 - 图1

图 11-1 Launcher启动应用程序的调用栈

从调用栈可以看出,Launcher在主线程中捕获了onClick事件,进而调用了Launcher.startActivitySafely方法启动应用程序Activity。

传给startActivitySafely方法的一个关键参数是Intent,它保存了要启动的应用程序主Activity的信息。以Settings应用程序为例,Intent存储的信息如下。

Action:android. intent.action.MAIN.

Categories:android. intent.category.LAUNCHER.

Component:要启动的Activity组件,值为com.android.settings/.Settings。

Flag:启动标记为0x102000000,即FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_FORWARD_RESULT。

Launcher. startActivitySafely方法中并没有特别的处理,最终调用Launcher从父类中继承而来的startActivity方法,Launcher的父类即Activity类。

startActivity方法定义于frameworks/base/core/java/android/app/Activity.java中。该方法很简单,只是对startActivityForResult方法的封装调用。startActivityForResult方法的代码如下:


/*options是Android 4.1中新加入的参数,用于接收一个由ActivityOptions

生成的值,该值用于指定启动Activity的自定义动画和缩放动画/

public void startActivityForResult(Intent intent, int requestCode, Bundle options){

if(mParent==null){//非子Activity。本例满足该分支

Instrumentation.ActivityResult ar=

mInstrumentation.execStartActivity(

this, mMainThread.getApplicationThread(),mToken, this,

intent, requestCode, options);

//对于requestCode<0的情况,ar为null

//startActivity传入的requestCode为-1,表示不需要返回结果

if(ar!=null){//获取启动Activity的返回结果

mMainThread.sendActivityResult(

mToken, mEmbeddedID, requestCode, ar.getResultCode(),

ar.getResultData());

}

if(requestCode>=0){

mStartedActivity=true;

}

}else{//子Activity

if(options!=null){

//最终也是调用mInstrumentation.execStartActivity方法

mParent.startActivityFromChild(this, intent, requestCode, options);

}else{

mParent.startActivityFromChild(this, intent, requestCode);

}

}

}


startActivityForResult方法内部需要调用Instrumentation的execStartActivity方法,执行启动应用程序Activity的任务。对于子Activity,则是通过startActivityFromChild方法间接调用了execStartActivity完成Activity的启动。

这里涉及mInstrumentation、mToken、mMainThread这三个对象。

mInstrumentation是Instrumentation类型的对象,用于监控应用程序和系统(主要指Activity Manager)的交互过程。启动应用程序Activity需要与ActivityManagerService交互,因此需要通过Instrumentation代理并监控启动请求的处理。

mToken是IBinder类型的对象,其本质是一个BinderProxy,通过该Binder代理可以访问当前Activity(本例为Launcher)在ActivityManagerService中对应的ActivityRecord。ActivityRecord记录了当前Activity的信息,ActivityManagerService需要获取这些信息。

mMainThread是ActivityThread类型的对象,表示应用程序主线程,本例中代表的是Launcher的主线程。ActivityThread有一个ApplicationThread类型的成员变量,定义了调度当前Activity的方法,可以通过mMainThread.getApplicationThread方法返回该成员变量。execStartActivity方法会将其传入ActivityManagerService,因此ActivityManagerService便可以通过ApplicationThread跨进程调度当前Activity(本例中为Launcher,调度的结果是暂停Launcher)。

execStartActivity方法位于frameworks/base/core/java/android/app/Instrumentation.java中,代码如下:


public ActivityResult execStartActivity(

Context who, IBinder contextThread, IBinder token, Activity target,

Intent intent, int requestCode, Bundle options){

IApplicationThread whoThread=(IApplicationThread)contextThread;

if(mActivityMonitors!=null){

……//通过ActivityMonitor对启动Activity进行检查

}

try{

intent.setAllowFds(false);

intent.migrateExtraStreamToClipData();

/通过ActivityManagerProxy进而调用ActivityManagerService的startActivity方法/

int result=ActivityManagerNative.getDefault().startActivity(

whoThread, intent,

intent.resolveTypeIfNeeded(who.getContentResolver()),

token, target!=null?target.mEmbeddedID:null,

requestCode,0,null, null, options);

checkStartActivityResult(result, intent);//检查是否启动成功

}catch(RemoteException e){

}

return null;

}


ActivityManagerNative. getDefault()实际是调用ServiceManager.getService("activity"),通过Binder机制获取名为activity的Java系统服务的代理对象,并封装到ActivityManagerProxy中返回给Client。这个Java系统服务即ActivityManagerService,因此这里最终调用了ActivityManagerService的startActivity方法,执行流程从Client转入Server。关于Binder的运行机制读者可参照第6章的内容,这里不赘述。

Launcher启动应用程序Activity在Client端的执行流程如图11-2所示。

第11章 应用程序Activity的启动和调度 - 图2

图 11-2 Launcher启动在Client端的执行流程

如图11-2所示,具体的流程说明如下:

1)Launcher主线程捕获onClick点击事件后,调用Launcher.startActivitySafely方法。Launcher.startActivitySafely内部调用了Launcher.startActivity方法,Launcher.startActivity进而调用Launcher的父类Activity的startActivity方法。

2)Activity. startActivity调用Activity.startActivityForResult方法,传入该方法的requestCode参数为-1,表示Activity启动成功后,不需要执行Launcher.onActivityResult方法处理返回结果。

3)启动Activity需要与系统ActivityManagerService组件交互,必须纳入Instrumentation的监控,因此需要将启动请求转交Instrumentation,即调用Instrumentation.execStartActivity方法。

4)Instrumentation. execStartActivity首先通过ActivityMonitor检查启动请求,然后调用ActivityManagerNative.getDefault得到ActivityManagerProxy代理对象,进而调用该代理对象的startActivity方法。

5)ActivityManagerProxy是ActivityManagerService的代理对象,因此其内部存储的是BinderProxy,调用ActivityManagerProxy.startActivity实质是调用BinderProxy.transact向Binder驱动发送START_ACTIVITY_TRANSACTION命令。Binder驱动将处理逻辑从Launcher所在进程切换到ActivityManagerService所在进程。

[1]四大组件包括Activity、Service、Content Provider和Broadcast Receiver,关于Activity的概念,可以参考Android开发者官方网站http://developer.android.com/reference/android/app/Activity.html。