内容简介: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)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 以太坊源码分析(36)ethdb源码分析
- [源码分析] kubelet源码分析(一)之 NewKubeletCommand
- libmodbus源码分析(3)从机(服务端)功能源码分析
- [源码分析] nfs-client-provisioner源码分析
- [源码分析] kubelet源码分析(三)之 Pod的创建
- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。