内容简介:Matrix是微信开源的APM工具,APKChecker是其中针对APK文件做静态分析的工具,是Matrix的一部分。上面两篇腾讯云的相关文章,介绍了Matrix&ApkChecker的一些基本功能。本文针对APKChecker的源码做一个简单的分析,聊一下该工具各个功能的实现原理。采用的是
Matrix-ApkChecker — Apk 分析减包利器
Matrix是微信开源的APM工具,APKChecker是其中针对APK文件做静态分析的工具,是Matrix的一部分。上面两篇腾讯云的相关文章,介绍了Matrix&ApkChecker的一些基本功能。本文针对APKChecker的源码做一个简单的分析,聊一下该 工具 各个功能的实现原理。采用的是 Matrix 工程,master分支4月16号的代码为分析源码。
依赖库
简单看一下工程目录和gradle配置的依赖关系。显然,前文链接中的ApkChecker的代码目录是matrix-apk-canary。
- libs文件夹中,有apktool的jar包,反编译工具在这里也是意料之中
- resources目录中,有android的framework jar包,分析APK文件一些基础数据类型在Android framework层中定义
- gradle配置中,可以看到依赖了常用的gson工具类、Android的common工具包、本地工程matrix-common
- 走读一下matrix-common,基本上可以判定该module主要是matrix公用的数据结构与工具组件
代码结构
从代码目录上看,它的包名目录定义的十分清晰,基本上可以一目了然:
- exception:异常定义
- job:工作任务,包含任务管理、配置和常量定义
- output:输出
- result:分析结果相关
- task:分析任务,基本可以通过任务命名直接对应上相关文档中的功能
- ApkChecker类:最后输出jar包的程序入口
简单梳理一下类图关系,能够进一步了解源码的设计思路。
可以看出,基本上可以分为四部分:
- 左侧是入口类和核心任务类,里面封装了主要的分析实现过程
- 上侧是基础的任务实现部分,图中省略了很多功能任务类,只列了三个,可以看到一个简单的继承关系,和一个工厂模式的处理
- 右侧是任务结果部分,可以看到主要有Json和html两种格式。这部分主要是针对每个任务的结果输出,同样是一个继承关系和相应的工厂处理
- 下侧是整个分析任务的结果输出,可以简单认为它是对TaskResult的整理和真正的结果文件化输出。依然是两种格式的继承关系和工厂处理
综合起来看,如果对执行流程感兴趣,去看ApkJob类的实现就好了;对每个分析功能感兴趣,去task包目录下找对应的功能实现类就好了;对最后分析结果是如何输出的感兴趣,可以查一下TaskResult和JobResult相关的实现就好了。
核心流程走读
这里简单分析入口类ApkChecker和任务管理类ApkJob。
ApkChecker
入口类只有不到一百行的代码,十分简洁。主要除了封装了main函数入口,还处理了输入参数异常情况下输出HELP提示的过程。
public static void main(String... args){
if (args.length > 0) {
ApkChecker m = new ApkChecker();
m.run(args);
} else {
System.out.println(INTRODUCT + HELP);
System.exit(0);
}
}
private void run(String[] args){
ApkJob job = new ApkJob(args);
try {
job.run();
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
很明显,main函数封装的主要是ApkJob,核心函数是它的run接口。此处注意,输入参数args,作为ApkJob的构造函数参数传入,想来是作为了该类的成员变量处理了。
ApkJob
该类有530行左右的代码,如前文类图描述的,成员属性包括了两个分析任务的ApkTask的列表、一个输出结果工具JobResult的列表。除此之外,还有前面传进来的参数args和相关的配置参数描述类JobConfig、一个多线程执行器ExecutorService。构造方法中基本上做一些初始化工作。
run函数比较函数流程比较易懂,只有十几行:
public void run()throws Exception {
if (parseParams()) {
ApkTask unzipTask = TaskFactory.factory(TaskFactory.TASK_TYPE_UNZIP, jobConfig, new HashMap<String, String>());
preTasks.add(unzipTask);
for (String format : jobConfig.getOutputFormatList()) {
JobResult result = JobResultFactory.factory(format, jobConfig);
if (result != null) {
jobResults.add(result);
} else {
Log.w(TAG, "Unknown output format name '%s' !", format);
}
}
execute();
} else {
ApkChecker.printHelp();
}
}
-
解析参数
解析参数函数涉及到代码量,比较大。因为支持配置文件和参数两种形式的参数,核心解析函数parseGlobalParams()长达126行,经过解析和校验后,在成功的情况下,jobConfig成员被参数设置好,用于后续的分析任务。
-
在预处理任务列表中添加一个解压任务
预处理任务列表在整个类中,只有此处添加了一个解压任务。之所以设计成列表,应该是考虑代码的可拓展性。从业务上说,在每项检查点的任务执行之前,要先把目标APK解压出来,也是应有之意。
-
处理配置的输出格式,将所有格式加入输出结果工具列表
可以理解是参数解析好之后,通过传入参数做的第一件事。就是先把输出工具处理好,为后续分析结果的输出做好准备。
-
执行execute 函数
预处理执行完,就是正经的任务分析流程了。
private void execute()throws Exception { try { for (ApkTask preTask : preTasks) { preTask.init(); TaskResult taskResult = preTask.call(); if (taskResult != null) { TaskResult formatResult = null; for (JobResult jobResult : jobResults) { formatResult = TaskResultFactory.transferTaskResult(taskResult.taskType, taskResult, jobResult.getFormat(), jobConfig); if (formatResult != null) { jobResult.addTaskResult(formatResult); } } } } for (ApkTask task : taskList) { task.init(); } List<Future<TaskResult>> futures = executor.invokeAll(taskList, timeoutSeconds, TimeUnit.SECONDS); for (Future<TaskResult> future : futures) { TaskResult taskResult = future.get(); if (taskResult != null) { TaskResult formatResult = null; for (JobResult jobResult : jobResults) { formatResult = TaskResultFactory.transferTaskResult(taskResult.taskType, taskResult, jobResult.getFormat(), jobConfig); if (formatResult != null) { jobResult.addTaskResult(formatResult); } } } } executor.shutdownNow(); for (JobResult jobResult : jobResults) { jobResult.output(); } Log.d(TAG, "parse apk end, try to delete tmp un zip files"); FileUtils.deleteDirectory(new File(jobConfig.getUnzipPath())); } catch (Exception e) { Log.e(TAG, "Task executor execute with error:" + e.getMessage()); throw e; } }该函数45行,分开看还是很好理解的。
- 第一个for循环。执行预处理列表任务,主要是解压目标APK的任务,并输出结果
- 中间两个for循环及相关部分。初始化参数配置指定的每个检查任务;executor多线程执行每个任务;将每个任务的检查结果依次添加到输出结果中;关闭executor
- 第三个for循环及相关部分。结果输出工具把分析结果按照参数指定的格式依次输出到文件中;删除解压的APK文件。
小结
这里主要分析了Matrix/ApkChecker的代码结构和主要执行流程。可以大致总结出几点:
- 总体上,代码在各层级命名、包划分、结构设计、函数实现等各方面可读性都很强
- 利用继承和组合特性,使用工厂模式,让代码可拓展性也不错。比如新增一个分析功能,只需要实现一个ApkTask和相关参数类型即可;新增一种输出格式,只需要新增JobResult/TaskResult相关子类和关系即可
- 参数解析部分代码有些冗余,不太利于拓展和阅读
下一篇会学习一下具体检查任务的实现。
以上所述就是小编给大家介绍的《Matrix-APKChecker源码分析(1)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Nature of Code
Daniel Shiffman / The Nature of Code / 2012-12-13 / GBP 19.95
How can we capture the unpredictable evolutionary and emergent properties of nature in software? How can understanding the mathematical principles behind our physical world help us to create digital w......一起来看看 《The Nature of Code》 这本书的介绍吧!