Application of the technique in the module dependency bytecode

background

In recent years, with the rapid development of mobile phone business, in order to meet the rapidly growing demands of the mobile phone end users and business functions, mobile end technology infrastructure from a single large project application, to the progressive development of modular, component-based direction. High moral map, for example, Android terminal has exceeded millions of lines of code level, over 100 modules to participate in the final build.

Imagine if there is no standard set of tools depend on detection and monitoring, before long, the module dependencies may be 乱成一锅粥.

From the perspective of the module Owner, dependency analysis Why so important?

  • As the module Owner, I first of all want to know "who rely on me? Dependent on what the interface." The only way to assess the impact of changes in the scope of this module, and the interface is reasonable exposure.

  • I would like to know "who rely on me? What are the external interface called" external capacity needed to be aware of.

From a global perspective, a healthy dependency structure, to prevent the "lower module" directly dependent on "upper module", but also to eliminate cyclic dependencies. By analyzing the global dependency, you can quickly locate unreasonable reliance exposed business problems in advance.

Therefore, the analysis is dependent on the development process is very important part.

Common mode-dependent analysis

Android relies mentioned analysis, the first thing that comes to mind may be the following program:

  • Analysis Gradle dependency tree.

  • Scan the code importstatement.

  • Use Android Studio comes with analysis functions.

We analyze these programs one by one:

1. Gradle dependency tree

Use ./gradlew :<module>:dependencies --configuration releaseCompileClasspath -qcommand, it is easy to get the module dependency tree, as shown:

Not difficult to find, this approach has two problems:

  • That dependency declaration, even if not used in the library code is also output to the result.

  • Only analyze the module level, not accurate to the method level.

2. Scan importthe statement

Scanning Java file import statements, you can get calling relationships between files (classes).

Because the module file (s) is very easy to obtain a correspondence relationship (the scan list). Therefore, to obtain the dependencies between document (s), i.e., a file was dependencies between modules (classes) level.

The program relies Gradle compared to the results of a scan to enhance the dimensions can be analyzed to a file (class) level. But it also has some disadvantages:

  • Unable to deal with the import situation *.

  • Scan "There import the corresponding class but do not use" scenario too inefficient (needs to be done to find the source string).

3. Use the IDE's own analysis

Trigger Android Studio menu "Analyze" -> "Analyze Dependencies" can get the data dependencies between modules method level. Figure:

Android Studio can accurately analyze the "method level," the relationship between the reference module, support the jump see in the IDE, also can scan a reference to the Android SDK.

The two programs are better than before, largely accurate. But it also has a few questions:

  • It takes a long time: a comprehensive analysis of all-source AMap, takes about 10 minutes.

  • The results can not be multiplexed into a third party can not create a visual dependency graph.

  • Analysis relies forward and reverse dependence, need to scan twice.

To summarize the above three options:

  • Gralde rely on engineering configuration, too coarse and results allowed.
  • "Import - Scan program" to get the file level-dependent but incomplete data.
  • Although the precise IDE scan results, data multiplexing difficult, inconvenient engineered.

Why use byte code analysis?

Construction of the Android flowchart, all of the Java source code and generated aapt R.java file, will be compiled into .class files, then the file is compiled as dex, the apk file finally generated through apkbuilder. .class file figure that is what we call the Java byte code, it is a binary Java source code to escape.

In Android end bytecode common scenarios comprising:

  • Bytecode instrumentation: for implementing the performance monitoring of the UI, memory, network, etc. modules.

  • Jar package Review: No source for the library, to achieve some simple logic modified by editing the bytecode.

Back to the topic of this article, why should analyze bytecode, not Java code or dex file?

Do not use Java code is because some library or jar aar way to provide, we can not get the source code. Dex file is not in use because it is not easy parsing tools. So parse the bytecode is almost our only choice.

How to analyze dependencies using bytecode?

To get the dependencies between modules, in fact, it is to get the dependencies between "module between class and class." And to determine the relationship between classes, the statement can be analyzed class bytecode.

1. What is the time to analyze?

Android build process to know the students, should not be unfamiliar to transform this task. It is a bytecode Hook inlet Android Gradle provided by the plug.

In transform this task, all bytecode files (including the tripartite library) input to Input format.

To JarInput, for example, analyze the file field, obtained the name of the module. File file parsing, this module can obtain all the byte code files.

class files in the module with the corresponding name and path, on the establishment of a correspondence between the module and the like, this is the first key data we get.

2. What tools analysis?

Java bytecode analytical tool, the most commonly used include Javassit, ASM, CGLib. ASM is a lightweight library, better performance, but requires direct operation JVM instructions. CGLib package of ASM is to provide a more advanced interface.

In contrast, Javassist much simpler, it is a Java-based API, without operator JVM instruction, but its performance is less (because Javassit adds a layer of abstraction). In the prototype phase of the project, in order to quickly verify the results, we prefer the Javassit.

3. What specific programs?

Look at a simple example, how to analyze the relationship between the following call this code:

1: package com.account;
2: import com.account.B;
3: public class A {
4:     void methodA() {
5:         B b = new B(); // 初始化了 Class B 的实例 b
6:         b.methodB();   // 调用了 b 的 methodB 方法
7:     }
8: }
复制代码

Step 1: Initialize the environment, loading bytecode that A.class, registration statement analyzer.


// 初始化 ClassPool,将字节码文件目录注册到 Pool 中。
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath('<class文件所在目录>')
// 加载类A
CtClass cls = pool.get("com.account.A");
// 注册表达式分析器到类A
MyExprEditor editor = new MyExprEditor(ctCls)
ctCls.instrument(editor)
复制代码

Step 2: Customize the expression parser, analysis class A (to parse the statement calls for example).

class MyExprEditor extends ExprEditor {
    @Override
    void edit(MethodCall m) {
        // 语句所在类的名称
        def clsAName = ctCls.name
        // 语句在哪个方法被调用
        def where = m.where().methodInfo.getName()
        // 语句在哪一行被调用
        def line = m.lineNumber
        // 被调用类的名称
        def clsBName = m.className
        // 被调用的方法
        def methodBName = m.methodName
    }
    // 省略其它解析函数 ...
}
复制代码

ExprEditor the edit (MethodCall m) Class A callback can intercept all method calls (MethodCall).

In addition to this embodiment of MethodCall parsing further parsing support new, new Array, ConstructorCall, FieldAccess, InstanceOf, casts, try-catch sentence.

End analytic Class A, we get A depends on B's information:


Class1 Class2 Expr method1 method2 lineNo
com.account.A com.account.B NewExpr methodA 5
com.account.A com.account.B methodCall methodA methodB 6

Simply explained as follows:

Class 5 line com.account.A (inner methodA method), calls the constructor com.account.B;

Line 6 Class com.account.A (inner methodA method), the call com.account.B methodB function;

This is the "level between classes and methods' data dependent. Binding "modules and classes," the correspondence relationship obtained in the first step, we finally get a "method of data dependent level between modules."

Based on these basic data, we can also customize the rules dependent detection, generate the global module dependency graph, etc., described herein not carried out.

summary

This paper describes module dependencies importance in the development process of analysis, the Android common dependency analysis program, from Gradle dependency tree analysis, Import scan, using the IDE analysis, to the last byte code parsing program gradually progressive. The more close to the source of the solution, is the more fundamental solution.

Germany focus on high technology, professional content to find more technical field trips

Guess you like

Origin juejin.im/post/5d3e67896fb9a07ea33c5edd