背景
近年では、携帯電話事業の急速な発展と、携帯電話のエンドユーザーとビジネス機能の急速に成長する需要を満たすために、モジュラー、コンポーネントベース方向の漸進的な発展への単一の大規模なプロジェクトのアプリケーションからモバイルエンド技術インフラストラクチャ、。高い道徳的なマップは、例えば、アンドロイド端末は、最終ビルドに参加する100個のモジュールを介して、コードレベルの何百万行を超えました。
ツールの標準的なセットが検出および監視に依存が存在しない場合乱成一锅粥前に長い、モジュールの依存関係があることも、想像してみてください。
モジュール所有者の視点、依存関係の分析から、なぜそれほど重要なの?
-
モジュールの所有者として、私が最初にすべての知りたい「私に頼る誰?何インターフェースに依存し。」このモジュールの範囲の変更の影響を評価するための唯一の方法、およびインターフェースは、合理的な暴露です。
-
私はを認識するために必要な外部容量「と呼ばれる外部インターフェースとは何ですか?私に依存している人」知っていただきたいと思います。
グローバルな視点から、健康な依存構造、「上位モジュール」に直接依存し、「下部モジュール」を防止するだけでなく、環状依存性を排除します。グローバルな依存関係を分析することにより、あなたはすぐに事前に不合理な信頼公開されたビジネス上の問題を見つけることができます。
そのため、分析は、開発プロセスは非常に重要な部分に依存しています。
コモンモード依存の解析
Androidは述べた分析を依存している、心に来る最初の事は、以下のようなプログラムです。
-
分析のGradleの依存関係ツリー。
-
コードのスキャン
import
声明を。 -
使用のAndroid Studioは、解析機能が付属しています。
私たちは、これらのプログラムを一つずつ分析します:
1. Gradleの依存関係ツリー
使用する./gradlew :<module>:dependencies --configuration releaseCompileClasspath -q
コマンドは、示すように、モジュールの依存関係ツリーを取得するのは簡単です:
見つけるのは難しいことではありません、このアプローチは、2つの問題があります。
-
ライブラリコードで使用されていない場合でも、その依存関係の宣言は、また、結果に出力されます。
-
唯一のメソッドレベルに正確ではないモジュール・レベルを、分析します。
2.スキャンimport
文を
Javaファイルのインポート・ステートメントをスキャンすると、ファイル(クラス)間の呼び出し関係を取得することができます。
モジュールファイル(複数可)の対応関係(スキャンリスト)を取得するのは非常に簡単ですので。したがって、文書(複数可)との間の依存性を得るために、即ち、ファイルは、モジュール(クラス)レベル間の依存性でした。
プログラムはファイル(クラス)のレベルに分析することができる大きさを向上させるスキャンの結果と比較してのGradle依存しています。しかし、それはまた、いくつかの欠点があります。
-
*輸入状況に対処することができません。
-
「そこ対応するクラスをインポートするが、使用していない」(元の文字列を検索するために行われる必要がある)あまりにも非効率的なシナリオをスキャンします。
3. IDEの独自の分析を使用してください
トリガーのAndroid Studioのメニュー「分析」 - >「依存関係を分析し、」モジュール方式のレベルの間のデータの依存関係を取得することができます。図:
Androidのメーカーは正確にも、AndroidのSDKへの参照をスキャンすることができ、IDEで見ジャンプをサポートし、「メソッドレベル、」参照モジュール間の関係を分析することができます。
2つのプログラムが、大部分は正確以前よりも優れています。しかし、それはまた、いくつかの質問があります。
-
それは長い時間がかかる:AMAPは、約10分かかり、すべてのソースの包括的な分析を。
-
結果は、視覚的な依存関係グラフを作成することはできません第三者に多重化することはできません。
-
分析は、前方に依存しており、依存性を逆に、二回スキャンする必要があります。
上記の3つのオプションを要約すると:
- Graldeは、エンジニアリングコンフィギュレーション、許可粗すぎるとの結果に依存しています。
- 「インポート - スキャンプログラムは、」ファイルレベルに依存しますが、不完全なデータを取得します。
- 正確なIDEスキャン結果が、データの多重化が難しい、不便を設計します。
なぜバイトコード分析を使用して?
Androidのフローチャートの構築、Javaソースコードの全てとR.javaファイルAAPT生成には、そのファイルがDEX、最終的apkbuilderによって生成されたAPKファイルとしてコンパイルされ、.classファイルにコンパイルされます。我々はJavaバイトコードを呼んでの.classファイルの図は、脱出するバイナリJavaソースコードです。
Androidの最終バイトコードの一般的なシナリオであって、
-
バイトコード計測:モジュールなど、UI、メモリ、ネットワークのパフォーマンス監視を実行するため。
-
ジャーパッケージレビュー:ライブラリのソースなし、バイトコードを編集することで変更され、いくつかの簡単なロジックを実現します。
戻るこの記事の話題に、なぜバイトコードではなく、JavaコードやDEXファイルを分析する必要がありますか?
Javaコードを使用しないでくださいいくつかのライブラリーまたはjar AARの方法が提供するので、我々はソースコードを取得することはできませんです。それは簡単に解析ツールではありませんので、DEXファイルが使用されていません。だから、解析するバイトコードは、ほとんど私たちの唯一の選択肢です。
バイトコードを使用して依存関係を分析する方法は?
モジュール間の依存関係を取得するには、実際には、それは間の依存関係を取得することです「クラスとクラス間のモジュール。」そして、クラス間の関係を決定するために、ステートメントは、クラスのバイトコードを分析することができます。
1.分析する時間は何ですか?
Androidのビルドプロセスでは、このタスクを変換するために不慣れであってはならない、学生を知っています。これは、プラグによって提供バイトコードフック入口アンドロイドのGradleです。
このタスク、(三者ライブラリを含む)すべてのバイトコードファイル入力形式の入力を変換します。
JarInputに、例えば、ファイルのフィールドを分析し、モジュールの名前を得ました。ファイルのファイルの解析、このモジュールは、すべてのバイトコードファイルを入手することができます。
対応する名前とパスを持つモジュール内のクラスファイルは、モジュールなどの対応を確立する上で、これは我々が得る最初の鍵データです。
2.どのようなツールの分析?
Javaバイトコード分析ツールは、最も一般的に使用されるJavassit、ASM、CGLIBが含まれます。ASMは、軽量ライブラリ、より良い性能ですが、直接操作JVM命令が必要です。ASMのCGLIBパッケージは、より高度なインタフェースを提供することです。
これとは対照的に、Javassistのははるかに簡単、それはオペレータのJVMの命令なしに、JavaベースのAPIですが、(Javassitは、抽象化の層を追加するため)その性能は少ないです。プロジェクトのプロトタイプの段階では、すぐに結果を確認するために、我々はJavassitを好みます。
3.具体的にどのようなプログラム?
次の呼び出しの間の関係にこのコードを解析する方法を、簡単な例を見てください:
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: }
复制代码
ステップ1:、環境を初期化し、ローディングバイトコードA.class、登録届出書アナライザ。
// 初始化 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)
复制代码
ステップ2:式パーサー、分析クラスA(例えば文のコールを解析する)をカスタマイズします。
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編集(methodCallでのM)クラスAのコールバックは、すべてのメソッド呼び出し(methodCallで)を傍受することができます。
さらに解析するサポート新しい、新しいアレイ、ConstructorCall、FieldAccess、INSTANCEOFを、解析するmethodCallでの本実施形態に加えてキャスト、のtry-catch文を。
エンド分析クラスA、我々は、AがBの情報に依存し得ます:
クラス1 | クラス2 | 式expr | 法1 | 法2 | LINENO |
---|---|---|---|---|---|
com.account.A | com.account.B | NewExpr | methodA | 5 | |
com.account.A | com.account.B | methodCallで | methodA | methodB | 6 |
次のように簡単に説明:
クラス5行com.account.A(内側methodA法)、コンストラクタcom.account.Bを呼び出します。
ライン6クラスcom.account.A(インナーmethodAメソッド)、コールcom.account.B methodB機能。
これは、依存クラスやメソッドのデータ間の「レベルです。「モジュールやクラス、」第一工程で得られた対応関係をバインディング、我々は最終的に取得する「モジュール間のデータ依存レベルの方法を。」
これらの基本的なデータに基づいて、我々はまた、本明細書中に行われていない説明し、ルールに依存検出をカスタマイズするなどグローバルモジュールの依存関係グラフを生成することができます。
概要
本論文では、分析の開発プロセスのモジュールの依存関係の重要性を説明し、Androidの一般的な依存関係の解析プログラム、Gradleの依存関係ツリー分析から、インポートは、プログラムは徐々に進歩的解析する最後のバイトコードに、IDEの分析を使用して、スキャンします。ソリューションの元に、より近く、より根本的な解決策です。