Instrumentation框架分析及其使用
本文旨在從Android系統原始碼出發,簡單梳理Instrumentation框架的行為及邏輯結構,供有興趣的同學一起學習
從am instrument談起
am instrument命令的執行
我們知道,命令列執行Android測試的命令是adb shell am instrument,這個命令是如何調起我們的測試程式碼來進行測試的呢,讓我們從am命令的處理原始碼來開始一步步的檢視吧。
am.java是android系統處理am命令的類,其位於/frameworks/base/cmds/am/src/com/android/commands/am/下,有Android原始碼的同學可以到相關目錄下自行檢視
onRun方法是am處理各個不同命令的分發處,我們可以看到am命令有多種用法,其中am instrumentation命令會呼叫runInstrument()方法
public void onRun() throws Exception {
mAm = ActivityManagerNative.getDefault();
if (mAm == null) {
System.err.println(NO_SYSTEM_ERROR_CODE);
throw new AndroidException("Can't connect to activity manager; is the system running?" );
}
String op = nextArgRequired();
if (op.equals("start")) {
runStart();
} else if (op.equals("startservice")) {
runStartService();
} else if (op.equals("stopservice")) {
runStopService();
} else if (op.equals("force-stop")) {
runForceStop();
} else if (op.equals("kill")) {
runKill();
} else if (op.equals("kill-all")) {
runKillAll();
} else if (op.equals("instrument")) {
runInstrument();
} else if (op.equals("broadcast")) {
sendBroadcast();
} else if (op.equals("profile")) {
runProfile();
} else if (op.equals("dumpheap")) {
runDumpHeap();
} else if (op.equals("set-debug-app")) {
runSetDebugApp();
} else if (op.equals("clear-debug-app")) {
runClearDebugApp();
} else if (op.equals("bug-report")) {
runBugReport();
} else if (op.equals("monitor")) {
runMonitor();
} else if (op.equals("hang")) {
runHang();
} else if (op.equals("restart")) {
runRestart();
} else if (op.equals("idle-maintenance")) {
runIdleMaintenance();
} else if (op.equals("screen-compat")) {
runScreenCompat();
} else if (op.equals("to-uri")) {
runToUri(0);
} else if (op.equals("to-intent-uri")) {
runToUri(Intent.URI_INTENT_SCHEME);
} else if (op.equals("to-app-uri")) {
runToUri(Intent.URI_ANDROID_APP_SCHEME);
} else if (op.equals("switch-user")) {
runSwitchUser();
} else if (op.equals("start-user")) {
runStartUserInBackground();
} else if (op.equals("stop-user")) {
runStopUser();
} else if (op.equals("stack")) {
runStack();
} else if (op.equals("lock-task")) {
runLockTask();
} else if (op.equals("get-config")) {
runGetConfig();
} else {
showError("Error: unknown command '" + op + "'");
}
}
以下是runInsturmentation方法的原始碼
private void runInstrument() throws Exception {
String profileFile = null;
boolean wait = false;
boolean rawMode = false;
boolean no_window_animation = false;
int userId = UserHandle.USER_CURRENT;
Bundle args = new Bundle();
String argKey = null, argValue = null;
IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
String abi = null;
String opt;
while ((opt=nextOption()) != null) {
if (opt.equals("-p")) {
profileFile = nextArgRequired();
} else if (opt.equals("-w")) {
wait = true;
} else if (opt.equals("-r")) {
rawMode = true;
} else if (opt.equals("-e")) {
argKey = nextArgRequired();
argValue = nextArgRequired();
args.putString(argKey, argValue);
} else if (opt.equals("--no_window_animation")
|| opt.equals("--no-window-animation")) {
no_window_animation = true;
} else if (opt.equals("--user")) {
userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
abi = nextArgRequired();
} else {
System.err.println("Error: Unknown option: " + opt);
return;
}
}
if (userId == UserHandle.USER_ALL) {
System.err.println("Error: Can't start instrumentation with user 'all'");
return;
}
String cnArg = nextArgRequired();
ComponentName cn = ComponentName.unflattenFromString(cnArg);
if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
InstrumentationWatcher watcher = null;
UiAutomationConnection connection = null;
if (wait) {
watcher = new InstrumentationWatcher();
watcher.setRawOutput(rawMode);
connection = new UiAutomationConnection();
}
float[] oldAnims = null;
if (no_window_animation) {
oldAnims = wm.getAnimationScales();
wm.setAnimationScale(0, 0.0f);
wm.setAnimationScale(1, 0.0f);
}
if (abi != null) {
final String[] supportedAbis = Build.SUPPORTED_ABIS;
boolean matched = false;
for (String supportedAbi : supportedAbis) {
if (supportedAbi.equals(abi)) {
matched = true;
break;
}
}
if (!matched) {
throw new AndroidException(
"INSTRUMENTATION_FAILED: Unsupported instruction set " + abi);
}
}
if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)) {
throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
}
if (watcher != null) {
if (!watcher.waitForFinish()) {
System.out.println("INSTRUMENTATION_ABORTED: System has crashed.");
}
}
if (oldAnims != null) {
wm.setAnimationScales(oldAnims);
}
}
該方法主要做了這麼幾件事:
- 解析引數並處理異常,目前支援的引數為(-w,-p,-r,-e,–no_window_animation,–no-window-animation,–user,–abi)
- 獲取測試包名和TestRunner,格式為測試包名/TestRunner
- 進行一些引數的邏輯處理(通常沒有使用到,可以暫不關注)
- 啟動TestRunner進行測試(mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi))
- 如果附帶了-w引數,會等待至執行完成,否則直接結束處理
各個指令含義解析:
- -w, 等待執行完成後才返回,否則直接返回(Instrumentation的執行在不同執行緒,不管是否帶該引數都會正確執行)
- -p, 帶1個引數,將一些配置寫入指定檔案(具體用處還未研究,後續有需要再補充)
- -r, 輸出原始的資料(具體用處還未研究,後續有需要再補充)
- -e, 帶兩個引數,將這兩個引數作為鍵值對傳遞給TestRunner,由TestRunner處理(後面會提到)
- –no_window_animation或–no-window-animation,執行Instrumentation過程中禁用動畫效果,執行完後會恢復
- –user, 帶1個引數,使用指定的uid執行(具體用處還未研究,後續有需要再補充)
- –abi, 帶1個引數,使用指定的abi執行(具體用處還未研究,後續有需要再補充)
mAm是一個IActivityManager的物件,呼叫其startInstrumentation方法開始處理Instrumentation,下面我們來看看ActivityManager相關的知識
ActivityManager相關知識
ActivityManager是android框架的一個重要部分,它負責一新ActivityThread程序建立,Activity生命週期的維護,下圖為這幾個類之間的層次關係:
在這張圖中,綠色的部分是在SDK中開放給應用程式開發人員的介面,藍色的部分是一個典型的Proxy模式,紅色的部分是底層的服務實現,是真正的動作執行者。這裡的一個核心思想是Proxy模式,關於代理模式相關知識,請參考(暫卻,後續補上)。以上僅是簡單的介紹了下者幾個類的關係,隨著我們上文的步伐,我們會一點點分析出am命令是如何讓Android系統跑起來測試用例的。
獲取ActivityManager
還記得之前在am命令中啟動Instrumentation的命令麼?對的就是這個mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)
其中的mAm為mAm = ActivityManagerNative.getDefault();
接下來便是要研究ActivityManagerNative.getDefault()了:
static public IActivityManager getDefault() {
return gDefault.get();
}
gDefault的定義是IActivityManager的一個單例物件
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;
}
};
獲取到名為activity的服務後,呼叫asInterface方法:
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
返回的是一個ActivityManagerProxy物件,然後按照原來的流程應該執行的是startInstrumentation方法
public boolean startInstrumentation(ComponentName className, String profileFile,
int flags, Bundle arguments, IInstrumentationWatcher watcher,
IUiAutomationConnection connection, int userId, String instructionSet)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
ComponentName.writeToParcel(className, data);
data.writeString(profileFile);
data.writeInt(flags);
data.writeBundle(arguments);
data.writeStrongBinder(watcher != null ? watcher.asBinder() : null);
data.writeStrongBinder(connection != null ? connection.asBinder() : null);
data.writeInt(userId);
data.writeString(instructionSet);
mRemote.transact(START_INSTRUMENTATION_TRANSACTION, data, reply, 0);
reply.readException();
boolean res = reply.readInt() != 0;
reply.recycle();
data.recycle();
return res;
}
將相關引數寫入打包後呼叫mRemote.transact方法,這個mRemote即初始化ActivityManagerProxy時傳入的IBinder物件,即ServiceManager.getService(“activity”)
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
可見ServiceManager會先從sCache快取中檢視是否有對應的Binder物件,有則返回,沒有則呼叫getIServiceManager().getService(name),那麼要獲取這個以activity命名的Service,它是在哪裡建立的呢?通過全域性搜尋,我們找到這個呼叫關係,由於中間的方法實在是太太太太太長了,大家有興趣的自己去看原始碼吧,其呼叫過程如下:zygote->main->new SystemServer().run()->[SystemServer]startBootstrapServices()->[SystemServer]mActivityManagerService.setSystemProcess()->[ActivityManagerService]ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true)
由此可見,這個名為mRemote的Binder對應的是ActivityManagerService,ActivityManagerService的transact方法繼承了Binder的實現:
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;
}
會呼叫onTransact方法:
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
if (code == SYSPROPS_TRANSACTION) {
// We need to tell all apps about the system property change.
ArrayList<IBinder> procs = new ArrayList<IBinder>();
synchronized(this) {
final int NP = mProcessNames.getMap().size();
for (int ip=0; ip<NP; ip++) {
SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia=0; ia<NA; ia++) {
ProcessRecord app = apps.valueAt(ia);
if (app.thread != null) {
procs.add(app.thread.asBinder());
}
}
}
}
int N = procs.size();
for (int i=0; i<N; i++) {
Parcel data2 = Parcel.obtain();
try {
procs.get(i).transact(IBinder.SYSPROPS_TRANSACTION, data2, null, 0);
} catch (RemoteException e) {
}
data2.recycle();
}
}
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
// The activity manager only throws security exceptions, so let's
// log all others.
if (!(e instanceof SecurityException)) {
Slog.wtf(TAG, "Activity Manager Crash", e);
}
throw e;
}
}
由於statusCode不為SYSPROPS_TRANSACTION會呼叫父類ActivityManagerNative的onTransact方法,方法由於statusCode很多,我們只挑選了符合我們要求的部分的原始碼:
case START_INSTRUMENTATION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
ComponentName className = ComponentName.readFromParcel(data);
String profileFile = data.readString();
int fl = data.readInt();
Bundle arguments = data.readBundle();
IBinder b = data.readStrongBinder();
IInstrumentationWatcher w = IInstrumentationWatcher.Stub.asInterface(b);
b = data.readStrongBinder();
IUiAutomationConnection c = IUiAutomationConnection.Stub.asInterface(b);
int userId = data.readInt();
String abiOverride = data.readString();
boolean res = startInstrumentation(className, profileFile, fl, arguments, w, c, userId,
abiOverride);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
}
在讀取出相應資料後呼叫startInstrumentation方法,開始執行Instrumentation
啟動Instrumentation
所以回到之前am命令的處理,實際呼叫的是ActivityManagerService的startInstrumentation方法。所以Instrumentation的啟動是由ActivityManagerService.startInstrumentation()方法完成的
public boolean startInstrumentation(ComponentName className,
String profileFile, int flags, Bundle arguments,
IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection,
int userId, String abiOverride) {
enforceNotIsolatedCaller("startInstrumentation");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startInstrumentation", null);
// Refuse possible leaked file descriptors
if (arguments != null && arguments.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Bundle");
}
synchronized(this) {
InstrumentationInfo ii = null;
ApplicationInfo ai = null;
try {
ii = mContext.getPackageManager().getInstrumentationInfo(
className, STOCK_PM_FLAGS);
ai = AppGlobals.getPackageManager().getApplicationInfo(
ii.targetPackage, STOCK_PM_FLAGS, userId);
} catch (PackageManager.NameNotFoundException e) {
} catch (RemoteException e) {
}
if (ii == null) {
reportStartInstrumentationFailure(watcher, className,
"Unable to find instrumentation info for: " + className);
return false;
}
if (ai == null) {
reportStartInstrumentationFailure(watcher, className,
"Unable to find instrumentation target package: " + ii.targetPackage);
return false;
}
int match = mContext.getPackageManager().checkSignatures(
ii.targetPackage, ii.packageName);
if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) {
String msg = "Permission Denial: starting instrumentation "
+ className + " from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingPid()
+ " not allowed because package " + ii.packageName
+ " does not have a signature matching the target "
+ ii.targetPackage;
reportStartInstrumentationFailure(watcher, className, msg);
throw new SecurityException(msg);
}
final long origId = Binder.clearCallingIdentity();
// Instrumentation can kill and relaunch even persistent processes
forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
"start instr");
ProcessRecord app = addAppLocked(ai, false, abiOverride);
app.instrumentationClass = className;
app.instrumentationInfo = ai;
app.instrumentationProfileFile = profileFile;
app.instrumentationArguments = arguments;
app.instrumentationWatcher = watcher;
app.instrumentationUiAutomationConnection = uiAutomationConnection;
app.instrumentationResultClass = className;
Binder.restoreCallingIdentity(origId);
}
return true;
}
該方法做了如下的事情:
- 檢查TestRunner是否存在
- 檢查TargetPackage是否存在
- 檢測簽名是否一致
- 強制關閉被測包
- 通過addAppLocked方法建立ProcessRecord
addAppLocked方法的原始碼如下:
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
String abiOverride) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(info.processName, info.uid, true);
} else {
app = null;
}
if (app == null) {
app = newProcessRecordLocked(info, null, isolated, 0);
mProcessNames.put(info.processName, app.uid, app);
if (isolated) {
mIsolatedProcesses.put(app.uid, app);
}
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
// This package really, really can not be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
info.packageName, false, UserHandle.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ info.packageName + ": " + e);
}
if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))
== (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) {
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
startProcessLocked(app, "added application", app.processName, abiOverride,
null /* entryPoint */, null /* entryPointArgs */);
}
return app;
}
之後會呼叫startProcessLocked方法啟動程序,啟動程序的過程就比較複雜了,暫時不去分析了,具體呼叫流程如下:startProcessLocked->Process.start->startViaZygote->zygoteSendArgsAndGetResult,zygoteSendArgsAndGetResult函式最終實現的,是向socket服務端寫書據,把建立程序的請求通過socket通訊方式讓framework的程序孵化類zygote建立新程序。而資料就是argsForZygote(一個以字串List形式的把Process.start()所有呼叫引數都包含在裡面的變數),具體的啟動過程可以參考:android程序建立分析
socket服務端收到建立新程序的請求,ZygoteConnection.runOnce()接收到新程序的引數,然後呼叫Zygote.forkAndSpecialize()來fork一個子程序,在子程序中會接著關閉socket,呼叫ZygoteInit.invokeStaticMain(cloader, className, mainArgs),即呼叫ActivityThread.main()。 新的應用程序會從ActivityThread 的 main()函式處開始執行。
ActivityThread,新的程序
首先來看入口,main函式:
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我們看到main方法初始化了主執行緒的訊息佇列,例項化了一個ActivityThread物件,然後呼叫了它的attach方法:
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
@Override
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
// Watch for getting close to heap limit.
BinderInternal.addGcWatcher(new Runnable() {
@Override public void run() {
if (!mSomeActivitiesChanged) {
return;
}
Runtime runtime = Runtime.getRuntime();
long dalvikMax = runtime.maxMemory();
long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
if (dalvikUsed > ((3*dalvikMax)/4)) {
if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
+ " total=" + (runtime.totalMemory()/1024)
+ " used=" + (dalvikUsed/1024));
mSomeActivitiesChanged = false;
try {
mgr.releaseSomeActivities(mAppThread);
} catch (RemoteException e) {
}
}
}
});
} else {
// Don't set application object here -- if the system crashes,
// we can't display an alert, we just want to die die die.
android.ddm.DdmHandleAppName.setAppName("system_process",
UserHandle.myUserId());
try {
mInstrumentation = new Instrumentation();
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate Application():" + e.toString(), e);
}
}
// add dropbox logging to libcore
DropBox.setReporter(new DropBoxReporter());
ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
synchronized (mResourcesManager) {
// We need to apply this change to the resources
// immediately, because upon returning the view
// hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
// This actually changed the resources! Tell
// everyone about it.
if (mPendingConfiguration == null ||
mPendingConfiguration.isOtherSeqNewer(newConfig)) {
mPendingConfiguration = newConfig;
sendMessage(H.CONFIGURATION_CHANGED, newConfig);
}
}
}
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int level) {
}
});
}
我們看到由於是非系統初始化(不是系統啟動時啟動的程序),傳入的引數為false,我們重點關注前面一半的邏輯。這裡又出現了
final IActivityManager mgr = ActivityManagerNative.getDefault();
有了之前的經驗我們已經知道這是指向的ActivityManagerService,然後呼叫了
mgr.attachApplication(mAppThread);
呼叫ActivityManagerService.attachApplication
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
接著走到attachApplicationLocked,這個方法比較長,為了節約篇幅,不貼原始碼了,會呼叫ActivityThread的bindApplication方法
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked());
而bindApplication做的事情是資料繫結併發送訊息(原始碼部分節選)
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableOpenGlTrace = enableOpenGlTrace;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
sendMessage(H.BIND_APPLICATION, data);
在handleMessage方法中可以看到接到Message後的處理
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
handleBindApplication方法實在是太長了,我們就擷取和Instrumentation相關的部分吧
if (data.instrumentationName != null) {
InstrumentationInfo ii = null;
try {
ii = appContext.getPackageManager().
getInstrumentationInfo(data.instrumentationName, 0);
} catch (PackageManager.NameNotFoundException e) {
}
if (ii == null) {
throw new RuntimeException(
"Unable to find instrumentation info for: "
+ data.instrumentationName);
}
mInstrumentationPackageName = ii.packageName;
mInstrumentationAppDir = ii.sourceDir;
mInstrumentationSplitAppDirs = ii.splitSourceDirs;
mInstrumentationLibDir = ii.nativeLibraryDir;
mInstrumentedAppDir = data.info.getAppDir();
mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
mInstrumentedLibDir = data.info.getLibDir();
ApplicationInfo instrApp = new ApplicationInfo();
instrApp.packageName = ii.packageName;
instrApp.sourceDir = ii.sourceDir;
instrApp.publicSourceDir = ii.publicSourceDir;
instrApp.splitSourceDirs = ii.splitSourceDirs;
instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs;
instrApp.dataDir = ii.dataDir;
instrApp.nativeLibraryDir = ii.nativeLibraryDir;
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) {
throw new RuntimeException(
"Unable to instantiate instrumentation "
+ data.instrumentationName + ": " + e.toString(), e);
}
mInstrumentation.init(this, instrContext, appContext,
new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
data.instrumentationUiAutomationConnection);
if (mProfiler.profileFile != null && !ii.handleProfiling
&& mProfiler.profileFd == null) {
mProfiler.handlingProfiling = true;
File file = new File(mProfiler.profileFile);
file.getParentFile().mkdirs();
Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
}
} else {
mInstrumentation = new Instrumentation();
}
if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
}
// Allow disk access during application and provider setup. This could
// block processing ordered broadcasts, but later processing would
// probably end up doing the same disk access.
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) {
List<ProviderInfo> providers = data.providers;
if (providers != null) {
installContentProviders(app, providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don't want that racing.
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
} finally {
StrictMode.setThreadPolicy(savedPolicy);
}
方法中首先初始化了mInstrumentation,此處Load的Class即am instrument命令傳入的TestRunner
java.lang