dalvik dex コンバーターとメソッドの呼び出しに使用しているオペコードに問題があります。基本的にprivate final、クラスで定義されたメソッドがあり、それを呼び出すと、invoke-directオペコードを生成する代わりに、dx が生成されinvoke-superます。プライベート メソッドであるため、スーパー クラスにはメソッドが存在しないため、デバイスで VFY 違反が発生します。これをトリガーする正確なシナリオを突き止めることができました。次の場合に発生するようです。
- JaCoCo を使用してクラスをインストルメント化し、
- でコンパイルされたクラス
--target 1.6
これら 2 つの条件が満たされている場合、結果の dex クラスにはinvoke-super代わりにinvoke-direct. JaCoCo を無効にすると、または でコンパイルすると--target 1.5、正しいinvoke-directオペコードが使用されます。
javap逆アセンブルされたクラス コードを見るdxと、直接ではなくスーパーを想定する原因がわかります。
インストルメント化されていない、1.6 用にコンパイルされたもの:
$ javap -d com.example.ClassName | grep waitForConnectivity
159: invokespecial #115; //Method waitForConnectivity:()V
$ dexdump -d classes.dex | grep waitForConnectivity
147ad8: 7010 6042 0200 |001e: invoke-direct {v2}, Lcom/example/ClassName;.waitForConnectivity:()V // method@4260
インストルメント化、1.5 用にコンパイル ( --target 1.5):
$ javap -d com.example.ClassName | grep waitForConnectivity
235: invokespecial #115; //Method waitForConnectivity:()V
$ dexdump -d classes.dex | grep waitForConnectivity
149d4c: 7010 9242 0400 |0018: invoke-direct {v4}, Lcom/example/ClassName;.waitForConnectivity:()V // method@4292
インストルメント化、1.6 用にコンパイル:
$ javap -d com.example.ClassName | grep waitForConnectivity
235: invokespecial #115; //Method com/example/ClassName.waitForConnectivity:()V
$ dexdump -d classes.dex | grep waitForConnectivity
149d4c: 6f10 9242 0400 |0018: invoke-super {v4}, Lcom/example/ClassName;.waitForConnectivity:()V // method@4292
したがって、違いは、コンパイルされた .class ファイルが、クラスの完全修飾クラス名を参照する Java バイトコードをコンパイルしたことです (" " と " "thisに注意してください)。メソッド名が完全修飾されている場合は を使用する必要があると自動的に想定しているように見えますが、修飾されていない場合は を使用します。//Method waitForConnectivity:()V//Method com/example/ClassName.waitForConnectivity:()Vdxinvoke-superinvoke-direct
私の質問は次のとおりです。
- これは Android の
dxバグですか、それとも JaCoCo のバグですか? - 自動化されたテスト ビルドで JaCoCo がインストルメント化されたクラスを適切に動作させるには、どうすればこれを回避できますか?
私の現在の回避策は、Maven の「jacoco」プロファイルを使用することです。そこで${java.version}プロパティをオーバーライドして、デフォルトの「1.6」から「1.5」に変更します。より良い解決策はありますか?