第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-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-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。