Android 向けの迅速なイテレーション開発
このページでは、bazel mobile-install
で反復型開発がどのように行われるかについて説明します。
大幅に向上させることができます従来のデータウェアハウスとの違いについて
課題に対処できます。
概要
Android アプリの小さな変更を非常にすばやくインストールする手順は次のとおりです。
- インストールするアプリの
android_binary
ルールを見つけます。 proguard_specs
属性を削除して Proguard を無効にします。multidex
属性をnative
に設定します。dex_shards
属性を10
に設定します。- (Dalvik ではなく)ART を実行するデバイスを USB 経由で接続し、USB を有効にします 表示されます。
bazel mobile-install :your_target
を実行します。アプリの起動に少し時間がかかる 通常より遅いです。- コードまたは Android リソースを編集する。
bazel mobile-install --incremental :your_target
を実行します。- 長時間待つ必要はありません。
役立つ可能性のある Bazel のコマンドライン オプションは次のとおりです。
--adb
は、どの adb バイナリを使用するかを Bazel に指示します--adb_arg
を使用すると、adb
のコマンドラインに追加の引数を追加できます。この機能の有用な用途の一つは、ワークステーションに複数のデバイスが接続されている場合に、インストールするデバイスを選択することです。bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target
--start_app
が自動的にアプリを起動します
はじめに
デベロッパーのツールチェーンの最も重要な属性の 1 つは速度です。コードを変更して 1 秒以内に実行されるものと、変更が想定どおりに機能するかどうかのフィードバックを得るまでに数分、場合によっては数時間も待たなければならないものでは、大きな違いがあります。
残念ながら、.apk をビルドするための従来の Android ツールチェーンには、多くのモノリシックな連続的なステップが含まれており、Android アプリをビルドするにはこれらをすべて行う必要があります。Google では、Google マップのような大規模なプロジェクトで、1 行の変更をビルドするために 5 分間待つのは珍しいことではありませんでした。
bazel mobile-install
により、Android の反復型開発が大幅に高速化されます。
変更のプルーニング、作業のシャーディング、巧妙な操作を組み合わせて、
Android の内部。アプリのコードを変更する必要はありません。
従来のアプリのインストールに関する問題
Android アプリのビルドには、次のような問題があります。
dex 変換。デフォルトでは、「dx」はビルドで 1 回だけ呼び出され、以前のビルドの作業を再利用する方法を認識していません。1 つのメソッドのみが変更された場合でも、すべてのメソッドを再度デックスします。
デバイスへのデータのアップロード。adb は USB 2.0 接続の帯域幅をすべて使用しないため、サイズの大きいアプリのアップロードには時間がかかることがあります。アプリ全体が 小さな部分のみが変更された場合でも、たとえば、リソースや 単一のメソッドであるため、これが大きなボトルネックになる可能性があります。
ネイティブ コードへのコンパイル。Android L では、新しい Android ランタイムである ART が導入されました。 これは、アプリをジャストインタイムでコンパイルするのではなく、 Dalvik。これにより、インストール時間が長くなるものの、アプリの速度が大幅に向上します。ユーザーはアプリをインストールすることが多いため、これは良いトレードオフになります。 何度でも再利用できますが、開発に時間がかかり、 インストールされ、各バージョンの実行回数は多くても数回程度です。
bazel mobile-install
のアプローチ
bazel mobile-install
により、次の改善が行われます。
シャーディングされた dex 変換。アプリの Java コードをビルドした後、Bazel でクラスをシャーディング ファイルをほぼ同じサイズに分割し、実行時に個別に
dx
を呼び出します。 できます。前回のビルド以降に変更されていないシャードでは、dx
が呼び出されません。増分ファイル転送。Android リソース、.dex ファイル、ネイティブ ライブラリはメインの .apk から削除され、別の mobile-install ディレクトリに保存されます。これにより、コードの更新や、Android アプリの リソースを個別に管理することもできます。したがって、次のようになります。 転送にかかる時間は短くなり、アップロード済みの .dex ファイルだけが デバイス上で再コンパイルされます。
アプリの一部を .apk の外部から読み込む。小さなスタブ アプリケーションは、 Android リソース、Java コード、ネイティブ コードを読み込む .apk に挿入する デバイス上のモバイル インストール ディレクトリからダウンロードし、コントロールを 使用します。ごくまれなケースを除き、すべてアプリに対して透過的である 以下で説明します。
シャーディングされた dex 変換
シャーディングされたデクストリングは比較的簡単です。.jar ファイルがビルドされると、ツールによって、ほぼ同じサイズの個別の .jar ファイルにシャーディングされ、前回のビルド以降に変更されたファイルに対して dx
が呼び出されます。このロジックは、
dex 変換するシャードが Android に固有のものではなく、
Bazel の一般的な変更プルーニング アルゴリズム。
シャーディング アルゴリズムの最初のバージョンでは、単純に .class ファイルを単純に リストを同じサイズに分割しましたが、 次善の策: クラスが追加または削除された場合(ネストされた、 すべてのクラスがアルファベット順で 1 つずつ移動し、 その結果、シャードに再度 dex 変換が行われます。そのため、個々のクラスではなく Java パッケージをシャーディングすることにしました。もちろん、新しいパッケージが追加または削除されると、多くのシャードがインデックスに登録されますが、これは単一クラスの追加または削除よりも頻度がはるかに低くなります。
シャードの数は BUILD ファイル(
android_binary.dex_shards
属性など)。Bazel は、理想的な環境であれば、
最適なシャードの数を自動的に決定しますが、Bazel は現在、
たとえば、ビルド中に実行するコマンドなど)を、
実行されないため、最適なシャード数を決定できません。
最終的に検索される Java クラスが
。一般的に、シャードの数が多いほどビルドが速くなり、
インストールには影響がありますが、動的パラメータによって、アプリの起動が遅くなります。
リンカーの作業量が増えますスイート スポットは通常 10 ~ 50 のシャードです。
増分ファイル転送
アプリをビルドしたら、次はインストールします。できるだけ手間をかけずにインストールしましょう。インストールは以下の手順で構成されます。
- .apk のインストール(通常は
adb install
を使用) - .dex ファイル、Android リソース、ネイティブ ライブラリを モバイル インストール ディレクトリ
最初のステップでは、インクリメンタリティはあまりありません。つまり、
判断できます現在、Bazel は、このステップを実行するかどうかの判断をユーザーに依存しています
--incremental
コマンドライン オプションを使用する。これは、
すべてのケースに対応します
第 2 ステップでは、ビルドに含まれるアプリのファイルがデバイス上のファイルと比較されます。 デバイス上にあるアプリファイルとそのファイルをリストしたマニフェスト ファイル チェックサムがあります新しいファイルはデバイスにアップロードされ、変更されたファイルは更新され、削除されたファイルはデバイスから削除されます。マニフェストが存在しない場合、すべてのファイルに 指定できます。
増分インストールのアルゴリズムは、 マニフェストではチェックサムではなく、デバイス上のファイルを変更できます。この場合、 コンピュータ上のファイルのチェックサムを計算して、 しかし、これはインストール時間の増加に見合わないと判断されました。
スタブ アプリケーション
スタブ アプリケーションでは、dex、ネイティブ コード、
デバイス上の mobile-install
ディレクトリにある Android リソースが発生する。
実際の読み込みは BaseDexClassLoader
のサブクラス化によって実装され、比較的よく文書化された手法です。この処理は、アプリケーションの
クラスが読み込まれて、APK 内にあるすべてのアプリ クラスが
更新できるように、デバイス上の mobile-install
ディレクトリに配置する
(adb install
なし)。
この処理は アプリケーションのクラスが読み込まれるので、アプリケーション クラスを .apk があります。この場合、これらのクラスに変更を加えるには、 再インストールします。
これは、AndroidManifest.xml
で指定された Application
クラスをスタブ アプリケーションに置き換えることで実現できます。この
アプリが起動されるタイミングを制御し、クラスローダーと
リソース マネージャー(コンストラクタ)に、
Android フレームワーク内部の Java リフレクション。
スタブ アプリケーションは、mobile-install によってインストールされたネイティブ ライブラリを別の場所にコピーします。これは、動的リンカーがファイルに X
ビットを設定する必要があるため必要です。これは、root 以外の adb
でアクセスできる場所では行えません。
これらすべての処理が完了すると、スタブ アプリケーションは
実際のApplication
クラスを指定し、それ自体へのすべての参照を実際の
実装する必要があります。
結果
パフォーマンス
一般的に、bazel mobile-install
を使用すると、小さな変更を加えた後の大規模なアプリのビルドとインストールが 4~10 倍速くなります。
いくつかの Google サービスについて、次の数値が計算されました。
これは、変更の性質によって異なります。ベース ライブラリを変更した後の再コンパイルは時間がかかります。
制限事項
スタブ アプリケーションが行うトリックは、すべてのケースで機能するとは限りません。次のようなケースでは、想定どおりに機能しない場合があります。
Context
がContentProvider#onCreate()
のApplication
クラスにキャストされる場合。このメソッドは、Application
クラスのインスタンスを置き換える前に、アプリの起動時に呼び出されます。そのため、ContentProvider
は実際のアプリではなく、スタブ アプリを参照します。Context
をこのようにダウンキャストすることは想定されていないため、これはバグではないと主張できますが、Google のいくつかのアプリで発生しているようです。bazel mobile-install
がインストールしたリソースは、次の内部からのみ使用できます クリックします。リソースに他のアプリがPackageManager#getApplicationResources()
の場合、これらのリソースは 最後の非増分インストールですART を実行していないデバイス。スタブ アプリケーションは Froyo 以降で正常に動作しますが、Dalvik にはバグがあり、Java アノテーションが特定の方法で使用されている場合など、特定のケースでコードが複数の .dex ファイルに分散されている場合、アプリが正しくないと判断されます。アプリがこれらのバグをトリガーしない限り、Dalvik でも動作するはずです(ただし、古いバージョンの Android のサポートは Google の重点ではありません)。