1. 准备

首先安装被测试程序和包含测试case的程序

包含测试case的程序的AndroidManifest中包含有这么一行配置:

其中targetPackage指定测试程序的包名

2.运行

adb shell am instrument -w com.android.foo.test/android.support.test.runner.AndroidJUnitRunner

会调用Am.java 中的runInstrument方法

private void runInstrument() throws Exception {

....

ComponentName cn;

if (cnArg.contains("/")) {

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();

}

if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)) {

throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());

}

}

cn是测试程序的component name

会创建InstrumentationWatcher用来监测instrumentation test运行的状态

会创建UiAutomationConnection用来接收UiAutomator的命令,运行一些shell进程才能进行的特权操作

最后会调用AMS的startInstrumentation

ActivityManagerService.java

public boolean startInstrumentation(ComponentName className,

String profileFile, int flags, Bundle arguments,

IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection,

int userId, String abiOverride) {

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) {

首先从className中找到InstrumentationInfo,得到target package的application info

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;

然后launch被测试的程序,将instrmentation的一些信息设置到ProcessRecord中

在被测程序的application创建后,调用AMS的attachApplicationLocked时

private final boolean attachApplicationLocked(IApplicationThread thread,

int pid) {

thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,

profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,

app.instrumentationUiAutomationConnection, testMode,

mBinderTransactionTrackingEnabled, enableTrackAllocation,

isRestrictedBackupMode || !normalMode, app.persistent,

new Configuration(mConfiguration), app.compat,

getCommonServicesLocked(app.isolated),

mCoreSettingsObserver.getCoreSettingsLocked());

}

会将app.instrumentationClass, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection传入。

在ActivityThread的handleBindApplication中

if (data.instrumentationName != null) {

try {

ii = new ApplicationPackageManager(null, getPackageManager())

.getInstrumentationInfo(data.instrumentationName, 0);

} catch (PackageManager.NameNotFoundException e) {

throw new RuntimeException(

"Unable to find instrumentation info for: " + data.instrumentationName);

}

mInstrumentationPackageName = ii.packageName;

mInstrumentationAppDir = ii.sourceDir;

mInstrumentationSplitAppDirs = ii.splitSourceDirs;

mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);

mInstrumentedAppDir = data.info.getAppDir();

mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();

mInstrumentedLibDir = data.info.getLibDir();

} else {

ii = null;

}

找出测试程序的InstrumentationInfo

// Continue loading instrumentation.

if (ii != null) {

final ApplicationInfo instrApp = new ApplicationInfo();

ii.copyTo(instrApp);

instrApp.initForUser(UserHandle.myUserId());

final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,

appContext.getClassLoader(), false, true, false);

final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

try {

final 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);

}

final ComponentName component = new ComponentName(ii.packageName, ii.name);

mInstrumentation.init(this, instrContext, appContext, component,

data.instrumentationWatcher, data.instrumentationUiAutomationConnection);

if (mProfiler.profileFile != null && !ii.handleProfiling

&& mProfiler.profileFd == null) {

mProfiler.handlingProfiling = true;

final File file = new File(mProfiler.profileFile);

file.getParentFile().mkdirs();

Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);

}

} else {

mInstrumentation = new Instrumentation();

}

创建AppContext加载测试程序中的android.support.test.runner.AndroidJUnitRunner

AndroidJUnitRunner继承自Instrumentation,同时赋给mInstrumentation

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);

调用Application的OnCreate之前,会调用AndroidJUnitRunner的onCreate

AndroidJUnitRunner.java

@Override

public void onCreate(Bundle arguments) {

super.onCreate(arguments);

setArguments(arguments);

specifyDexMakerCacheProperty();

start();

}

public void start() {

if (mRunner != null) {

throw new RuntimeException("Instrumentation already started");

}

mRunner = new InstrumentationThread("Instr: " + getClass().getName());

mRunner.start();

}

start()方法启动一个新的线程,然后调用AndroidJUnitRunner的onStart方法

@Override

public void onStart() {

super.onStart(); //等待UI 线程变成 idle

if (getBooleanArgument(ARGUMENT_DEBUG)) {

Debug.waitForDebugger();

}

setupDexmakerClassloader();

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

PrintStream writer = new PrintStream(byteArrayOutputStream);

List listeners = new ArrayList();

try {

JUnitCore testRunner = new JUnitCore();

addListeners(listeners, testRunner, writer);

TestRequest testRequest = buildRequest(getArguments(), writer);

Result result = testRunner.run(testRequest.getRequest());

result.getFailures().addAll(testRequest.getFailures());

} catch (Throwable t) {

// catch all exceptions so a more verbose error message can be displayed

writer.println(String.format(

"Test run aborted due to unexpected exception: %s",

t.getMessage()));

t.printStackTrace(writer);

} finally {

Bundle results = new Bundle();

reportRunEnded(listeners, writer, results);

writer.close();

results.putString(Instrumentation.REPORT_KEY_STREAMRESULT,

String.format("\n%s",

byteArrayOutputStream.toString()));

finish(Activity.RESULT_OK, results);

}

}

创建JUnitCore以运行Junit程序

buildRequest创建测试请求

通过JUnitCore运行request

TestRequest buildRequest(Bundle arguments, PrintStream writer) {

// only load tests for current aka testContext

// Note that this represents a change from InstrumentationTestRunner where

// getTargetContext().getPackageCodePath() was also scanned

TestRequestBuilder builder = createTestRequestBuilder(writer,

getContext().getPackageCodePath());

...

return builder.build(this, arguments);

}

buildRequest通过TestRequestBuilder根据测试参数来创建TestRequest,我们主要看如果没有参数的情况

TestRequestBuilder.java

public TestRequest build(Instrumentation instr, Bundle bundle) {

if (mTestLoader.isEmpty()) {

// no class restrictions have been specified. Load all classes

loadClassesFromClassPath();

}

Request request = classes(instr, bundle, mSkipExecution, new Computer(),

mTestLoader.getLoadedClasses().toArray(new Class[0]));

return new TestRequest(mTestLoader.getLoadFailures(), new LenientFilterRequest(request, mFilter));

}

从测试apk中找到测试类

private void loadClassesFromClassPath() {

Collection classNames = getClassNamesFromClassPath();

for (String className : classNames) {

mTestLoader.loadIfTest(className);

}

}

android instrumentation 原理,Android 运行Instrumentation Test的原理分析相关推荐

  1. Android 系统性能优化(21)---App启动原理分析及启动时间优化

    一.启动原理解析 Android是基于Linux内核的,当手机启动,加载完Linux内核后,会由Linux系统的init祖先进程fork出Zygote进程,所有的Android应用程序进程以及系统服务 ...

  2. 【Android 安全】Android 应用 APK 加固总结 ( 加固原理 | 应用加固完整的实现方案 | 源码资源 )

    文章目录 一. APK 加固原理 1. Android 应用反编译 2. ProGuard 混淆 3. 多 dex 加载原理 4. 代理 Application 开发 5.Java 工具开发 6.Ap ...

  3. android 皮肤方案,Android布局流程及换肤原理

    Android 的布局流程 不考虑AMS binder机制,那么Android 的布局流程的最开始的入口(把前面的当作一个黑盒子,那么后续动作的第一个入口),则是在ActivityThread的per ...

  4. Android 10.0 PackageManagerService(一)工作原理及启动流程-[Android取经之路]

    摘要:PackageManagerService是Android系统核心服务之一,在Android中的非常重要,主要负责APK.jar包等的管理. 阅读本文大约需要花费50分钟. 文章的内容主要还是从 ...

  5. android 动画 最顶层_【Android编程实战】StrandHogg漏洞复现及原理分析_Android系统上的维京海盗...

    0x00 StrandHogg漏洞详情 StrandHogg漏洞 CVE编号:暂无 [漏洞危害] 近日,Android平台上发现了一个高危漏洞 该漏洞允许攻击者冒充任意合法应用,诱导受害者授予恶意应用 ...

  6. android中多态的应用_动态代理原理及在 Android 中的应用

    code小生 一个专注大前端领域的技术平台公众号回复Android加入安卓技术群 作者:trampcr 链接:https://www.jianshu.com/p/492903ab2fae 声明:本文已 ...

  7. Android界面性能优化最全总结、原理剖析

     界面是 Android 应用中直接影响用户体验最关键的部分.如果代码实现得不好,界面容易发生卡顿且导致应用占用大量内存. 我司这类做 ROM 的公司更不一样,预装的应用一定要非常流畅,这样给客户 ...

  8. Android判断App前台运行还是后台运行(运行状态)

    原文:http://p.codekk.com/detail/Android/wenmingvs/AndroidProcess AndroidProcess 项目地址:https://github.co ...

  9. Android端消息推送总结:实现原理、心跳保活、遇到的问题等

    前言 最近研究Android推送的实现, 研究了两天一夜, 有了一点收获, 写下来既为了分享, 也为了吐槽. 需要说明的是有些东西偏底层硬件和通信行业, 我对这些一窍不通, 只能说说自己的理解. 为什 ...

最新文章

  1. android 保存textview,为什么没有TextView(带ID)会自动保存它的状态?
  2. python队列线程池_实例详解:python高级编程之消息队列(Queue)与进程池(Pool)
  3. custom Idp sapdev 申请用户
  4. SAP Spartacus cost center list class的赋值逻辑
  5. 130242014045 林承晖 第2次实验
  6. mysql内连接的自连接_mysql 内连接、外连接、自连接
  7. 图神经网络的可解释性
  8. 【报告分享】2022年元宇宙全球年度(202页干货):蓄积的力量-北京大学.pdf(附下载链接)...
  9. 360文件管理器android,360文件管理器
  10. vmware vsphere安装与使用
  11. StyleGAN3 笔记
  12. 常用Firefox插件大全
  13. 80后小学计算机课上的游戏,80后最值得回味的15个经典课间游戏(组图)
  14. 计算机视觉教程2-6:八大图像特效算法制作你的专属滤镜(附Python代码)
  15. Pycharm代码docker容器运行调试 | 机器学习系列
  16. 最新版 Let’s Encrypt免费证书申请步骤,保姆级教程
  17. 你知道如何使用Java将DWG / DXF CAD文件转换为图像格式吗?
  18. 这个代码是我见过最牛逼的代码
  19. 单片机8位抢答器实训机电报告_16路抢答器单片机实训报告.docx
  20. JAVA打包软件exe4j使用教程

热门文章

  1. echart部分属性说明
  2. 常用的第三方框架汇总
  3. 阿姨家弟弟跟你差不多大,怎么还一颗都没掉呢?”马晓娟问。
  4. Android中自定义视图View之---前奏篇
  5. 使用小乌龟TortoiseGit上传代码到gitee仓库
  6. 第8课 如何使用开发环境命令行注册EOS靓号?
  7. ButterKnife使用方法详解
  8. 表白来信流量主小程序开发
  9. 关于调用系统相机以及压缩照片
  10. SMC_TRAFO_5Axes (FB)