どうもMMです。

気になる技術的な内容についてツラツラと書いていきたいと思っています。

Introduction


AndroidやiOSでは、アプリのソースを記述、IDE(統合開発環境)でビルド、パッケージ化を行って、初めてアプリケーションファイルが生成できます。

しかし、昨年のGoogle I/Oで発表された新機能であるAndroid App BundleとDynamic feature modulesの2つが大規模アプリケーションにおけるアプリの容量問題を解決してくれるものになりそうでしたので、ご紹介したいと思います。

 

Android App Bundle


Android App Bundleはリソースやネイティブのライブラリを分離し、Google Play Store側で端末ごとに適切なリソースを含んだapkを提供できる機能です。 具体的には、以下の3つの種類のAPKがAndroid App Bundle(aabファイル)には含まれることになります。

  • Base APK
    基本的な機能を含んだapk
  • Configuration APKs
    言語リソースや画面濃度、ネイティブライブラリ、CPU言語を含んだapk
  • Dynamic feature APKs
    後述するDynamic feature modulesを含んだapk

 

今まで個別のapkを自前で作成して配信していた作業が、Google Play Store側でよしなにやってくれるわけです!!(キタコレ)

また、この機能は4系など古いOSバージョンも対応しているようです。(ただしOS 4.4以下は自動的に端末に最も適切なapkをGoogle Play Store側で生成して配信されるようです)

この仕組みの大きなメリットは、ユーザーの端末へのダウンロード量を削減出来るようになることです。aabによって生成されるapkファイルは今回公開されたbundletoolで簡単に確認することができます。

https://github.com/google/bundletool

 

なお、このbundleToolは、apkの生成にも利用でき、これをレポジトリに含めてCIでビルド、Deploygateなどの配信サービスでデプロイといったことも可能になります。

bundletool build-apks --bundle=/MyApp/my_app.aab --output=/MyApp/my_app.apks //apkファイルのbuild

 

詳細は以下のリンクをご覧ください。

Test Android App Bundles with bundletool  |  Android Developers

Android App Bundle の導入


生成されたaabファイルを通常のapkと同じようにGoogle Play Storeにアップロードすると、最適なapkが配信されるようになりますが、本当にユーザーがapkを受け取れるのかを確認するには、今回から導入されたInternal test にまずはデプロイすることが推奨されています(Test Flight的な感じ?)。Internal testは、alphaの更に前のテストに利用できるテスト機能になり、実際にデプロイしてから約1分でテスターに配信されるのが特徴になります。

 

Dynamic Feature Modules


Android App Bundleも良い機能なのですが、Dynamic Feature Modulesはさらに利用価値がありそうなものになります。

Dynamic Feature Modulesは、特定の機能とリソースをベースモジュールから分離して、必要な時にユーザーがダウンロードして利用できる機能です。Dynamic Feature Modulesを普段使わない機能に適用すると、アプリサイズを劇的に減らすことが可能になるのではないかと考えています。

例えば本人認証などで、ある条件を満たした一部のユーザーしか利用しないものの、免許証や保険証のスキャンなどの機能が必要とされるような機能が必要となった場合、通常多くのユーザーは使うことのない機能にも関わらず、現状のアプリでは、結果として大きなサイズ(場合によっては10MB近く)のアプリとして提供しなければなりません。
ところが、Dynamic Feature Modulesを利用することで、このような特定の機能だけを必要なユーザーだけダウンロードさせるようにすることで、通常利用の多くのユーザーが利用するアプリにおいて、大幅なサイズの削減ができるはずです。

※ただし、こちらの機能は、OS 5.0(APIレベル21)以上でしか利用できないため、ご注意ください。

 

Dynamic Feature Modulesの導入


Dynamic Feature Modulesの設定は、ほぼ Android Library を作る工程と同じになルようです。異なる点は、Dynamic Feature Modules内の AndroidManifest.xml に、以下の設定を追加する必要があることになります。

  • Dynamic Deliveryに対応するかどうか(ここをtrueにすると分割してリリースできますが、逆にfalseにすると、最初からBase apkに含まれた形でリリースされるので、分割したいならtrueにします)
  • Moduleのダウンロード時に表示されるタイトル。Base apkのstring.xmlに定義する必要があります。
  • OS4.4以下のapkにこのモジュールを含むかどうか(このプロパティをtrueに設定した動的機能モジュールのみがapkに組み込まれます)
//AndroidManifest.xml
<dist:module
   dist:onDemand="true"  // Dynamic Deliveryに対応するかどうか
   dist:title="@string/name_of_module" // Module DL時に表示されるタイトル。baseのstrings.xmlに定義が必要
   <dist:fusing dist:include="false" />  // 4.4以下のapkにこのモジュールを含むかどうか
</dist:module>

また、com.android.library の代わりに com.android.dynamic-feature を追加します。

//bundle.gradle
apply plugin: 'com.android.dynamic-feature'

dependencies {
    implementation project(':app')
}

またBase apkになる方の AndroidManufest.xml にはDynamic Feature Modulesの依存を追加します。

//app/bundle.gradle
android {
  dynamicFeatures = [":name_of_module"]
}

 

Android Studioから直接追加することも可能なようです。

Edit -> New modulesで “Dynamic Feature Module” を作る

On demandの設定を追加

Dynamic Feature Moduleの設定されたモジュールをダウンロードするにはPlay Core Libraryが必要なようです。Play Core Libraryは、ダウンロード中の処理や失敗時の処理などをCallbackで定義出来るだけでなく、モジュールがダウンロードされているかなどの状態を管理できるライブラリのようです。

private val listener = SplitInstallStateUpdatedListener { state ->
        state.moduleNames().forEach { name ->
            when (state.status()) {
                SplitInstallSessionStatus.DOWNLOADING -> {
                    displayLoadingState(state, "Downloading $name")
                }
                ...
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    manager = SplitInstallManagerFactory.create(this)
}
override fun onResume() {
    manager.registerListener(listener)
    super.onResume()
}

 

結論:小さく出来そうだけど、設計が肝


色々見てみた結果、Android App Bundleでリソースファイルを切り離すことで削減したり、Dynamic Feature Modulesを使うことで、そもそものアプリを小さくすることは出来そうです。しかしながら、機能レベルで依存性/モジュール化を厳密に設計を行わないと破綻してしまうので、より設計/構成が重要になると思います。

つまり設計者の力量が全て

便利な機能である一方で設計者の力量に大きく左右されるものになりそうです。

 

誰か使ってみてください。