Android不仅可以装载众多的系统组件,还可以将它们跨进程组成ActivityTask,这个特性使得每个应用都不是孤立的。
activity task
从数据结构角度看,Task有先后之分,源码实现上采取了stack的方式。这种方式不仅符合用户的逻辑思维和使用习惯,还可以极大复用了系统的资源。
如图,contacts和SMS分属两个进程,当我们在联系人详情activity中调起了短信进程的短信编辑activity,那么这两个activity就被放到同一个task里面了。这是栈管理activity的方式。
但是ActivityTask不能算严格意义的Stack,默认情况和栈是一致的,但是Android提供了更多的操作方式。
affinity即喜好的意思,它代表这个activity希望归属的task,默认情况同一个程序有共同的affinity,<AndroidManifest.xml>中声明的Package Name。我们也可以主动在中使用taskAffinity标签属性来指定整个应用程序的Affinity。
一个activity task的affinity取决于它的根activity
在默认情况下,目标Activity将与startActivity的调用者处于同一Task中。但如果用户特别指定了FLAG_ACTIVITY_NEW_TASK,表明它希望为Activity重新开设一个Task。
这时就有两种情况:假如当前已经有一个Task,它的affinity与新Activity是一样的,那么系统会直接用此Task来完成操作,而不是另外创建一个Task;否则系统需要重启一个Task。
如果Activity指定了allowTaskReparenting,且后期程序的Task转为前台,它会被移动到和它更“亲近”的task中。
还有一种就是我们常见到的android:launchMode了,比较基础,这里就不展开了。
还有Intent的flag,具体可以查看源码或者官方文档。
Instrumentation机制
Base class for implementing application instrumentation code. When running with instrumentation turned on, this class will be instantiated for you before any of the application code, allowing you to monitor all of the interaction the system has with the application. An Instrumentation implementation is described to the system through an AndroidManifest.xml’s tag.
https://developer.android.com/reference/android/app/Instrumentation
Android系统在/system/bin下提供了很多“中介”程序让用户间接使用system server,例如pm、am等。
Instrumentation是ActivityManager的核心功能之一。
这是官方给出的Instrumentation的adb使用方法:
"-e"用于指定额外参数,这里给个使用例子:
adb shell am instrument -e class com.lxs.instrument.TestCase -w com.lxs.instrument /android.test.InstrumentationTestRunner
下面分析下它的执行"-e"和 [component]处理流程。
"am instrument"命令由runInstrument的am.java负责解析,和 [component]相关的代码如下:
//cnArg 就是Component字符串
String cnArg = nextArgRequired();
//unflatten规则就是以“/”为分隔符,前部分是package,剩余是Class。
ComponentName cn = ComponentName.unflattenFromString(cnArg);
“-e”后面所有的[name value]被保存到一个Bundle对象中,和其它参数一起传给AMS,AMS在承载它的进程进行关联后,由BindApplication的ActivityThread处理:
/*frameworks/base/core/java/android/app/ActivityThread.java*/
private void handleBindApplication(AppBindData data) {…
if (data.instrumentationName != null) {//instrumentationName是一个ComponentName对象
InstrumentationInfo ii = null;
try {
ii = appContext.getPackageManager().
getInstrumentationInfo(data.instrumentationName, 0);
} catch (PackageManager.NameNotFoundException e) {
}
…
mInstrumentationPackageName = ii.packageName;
…
LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false);
ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
try {
java.lang.ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {…
}
mInstrumentation.init(this, instrContext, appContext,
new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
data.instrumentationUiAutomationConnection);
…
} else {
mInstrumentation = new Instrumentation();
}
…
try {
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
…
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {…
}
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {…
}
} finally {
StrictMode.setThreadPolicy(savedPolicy);
}
}
AppBindData保存了和应用启动相关的所有关键数据。data.instrumentationArgs
对应的是“am instrument”命令中“-e”所指定的额外参数;而data.instrumentationName
对应的是“am instrument”命令中的 < COMPONENT >,我们可以根据这个参数构造出用户指定的Instrumentation对象(例如android.test.InstrumentationTestRunner)。当然,如果应用程序并没有Instrumentation组件,那么系统会使用默认的Instrumentation类。
由handleBindApplication的处理可以知道,一个Instrumentation对象的生命周期包括但不限于:
newInstance-> init-> onCreate->onStart-> callApplicationOnCreate->callActivityOnCreate
->callActivityXXX->onDestroy->…
Instrumentation提供了一种允许用户获取/改变应用和系统之间的交互流程的机制,自动化测试框架只是其中一种应用形式。InstrumentationTestRunner的原理框架如图:
如果用户没有指定Instrumentation的话,它的很多行为是“伪实现”,来看下:
/*frameworks/base/core/java/android/app/Instrumentation.java*/
public void onCreate(Bundle arguments) {//空实现
}
public void callActivityOnPause(Activity activity) {
activity.performPause();//默认情况下,将直接调用Activity元素的onPause
}
它们之间的关系:
Instrumentation和ActivityThread运行于同一进程之中,那么它们是否还处于同一个线程呢?
默认情况是肯定的,但是Instrumentation提供了一个方法:
/*frameworks/base/core/java/android/app/Instrumentation.java*/
public void start() {
if (mRunner != null) {
throw new RuntimeException("Instrumentation already started");
}
mRunner = new InstrumentationThread("Instr: " + getClass().getName());
mRunner.start();
}
通过这个方法,会运行在新开的一个线程中。
参考
《深入理解Android内核设计思想》