android instrumentation 原理,Android 运行Instrumentation Test的原理分析
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的原理分析相关推荐
- Android 系统性能优化(21)---App启动原理分析及启动时间优化
一.启动原理解析 Android是基于Linux内核的,当手机启动,加载完Linux内核后,会由Linux系统的init祖先进程fork出Zygote进程,所有的Android应用程序进程以及系统服务 ...
- 【Android 安全】Android 应用 APK 加固总结 ( 加固原理 | 应用加固完整的实现方案 | 源码资源 )
文章目录 一. APK 加固原理 1. Android 应用反编译 2. ProGuard 混淆 3. 多 dex 加载原理 4. 代理 Application 开发 5.Java 工具开发 6.Ap ...
- android 皮肤方案,Android布局流程及换肤原理
Android 的布局流程 不考虑AMS binder机制,那么Android 的布局流程的最开始的入口(把前面的当作一个黑盒子,那么后续动作的第一个入口),则是在ActivityThread的per ...
- Android 10.0 PackageManagerService(一)工作原理及启动流程-[Android取经之路]
摘要:PackageManagerService是Android系统核心服务之一,在Android中的非常重要,主要负责APK.jar包等的管理. 阅读本文大约需要花费50分钟. 文章的内容主要还是从 ...
- android 动画 最顶层_【Android编程实战】StrandHogg漏洞复现及原理分析_Android系统上的维京海盗...
0x00 StrandHogg漏洞详情 StrandHogg漏洞 CVE编号:暂无 [漏洞危害] 近日,Android平台上发现了一个高危漏洞 该漏洞允许攻击者冒充任意合法应用,诱导受害者授予恶意应用 ...
- android中多态的应用_动态代理原理及在 Android 中的应用
code小生 一个专注大前端领域的技术平台公众号回复Android加入安卓技术群 作者:trampcr 链接:https://www.jianshu.com/p/492903ab2fae 声明:本文已 ...
- Android界面性能优化最全总结、原理剖析
界面是 Android 应用中直接影响用户体验最关键的部分.如果代码实现得不好,界面容易发生卡顿且导致应用占用大量内存. 我司这类做 ROM 的公司更不一样,预装的应用一定要非常流畅,这样给客户 ...
- Android判断App前台运行还是后台运行(运行状态)
原文:http://p.codekk.com/detail/Android/wenmingvs/AndroidProcess AndroidProcess 项目地址:https://github.co ...
- Android端消息推送总结:实现原理、心跳保活、遇到的问题等
前言 最近研究Android推送的实现, 研究了两天一夜, 有了一点收获, 写下来既为了分享, 也为了吐槽. 需要说明的是有些东西偏底层硬件和通信行业, 我对这些一窍不通, 只能说说自己的理解. 为什 ...
最新文章
- android 保存textview,为什么没有TextView(带ID)会自动保存它的状态?
- python队列线程池_实例详解:python高级编程之消息队列(Queue)与进程池(Pool)
- custom Idp sapdev 申请用户
- SAP Spartacus cost center list class的赋值逻辑
- 130242014045 林承晖 第2次实验
- mysql内连接的自连接_mysql 内连接、外连接、自连接
- 图神经网络的可解释性
- 【报告分享】2022年元宇宙全球年度(202页干货):蓄积的力量-北京大学.pdf(附下载链接)...
- 360文件管理器android,360文件管理器
- vmware vsphere安装与使用
- StyleGAN3 笔记
- 常用Firefox插件大全
- 80后小学计算机课上的游戏,80后最值得回味的15个经典课间游戏(组图)
- 计算机视觉教程2-6:八大图像特效算法制作你的专属滤镜(附Python代码)
- Pycharm代码docker容器运行调试 | 机器学习系列
- 最新版 Let’s Encrypt免费证书申请步骤,保姆级教程
- 你知道如何使用Java将DWG / DXF CAD文件转换为图像格式吗?
- 这个代码是我见过最牛逼的代码
- 单片机8位抢答器实训机电报告_16路抢答器单片机实训报告.docx
- JAVA打包软件exe4j使用教程