複数のディメンションを持つ APK を複数作成する

アプリを Google Play に公開する場合は、Android App Bundle を作成してアップロードします。そうすることで、各ユーザーのデバイス構成に合わせて最適化された APK が Google Play によって自動的に生成、配信されるため、ユーザーはアプリの実行に必要なコードとリソースをダウンロードするだけで済みます。複数の APK を公開するという方法は、Google Play に公開しない場合は便利ですが、各 APK のビルド、署名、管理を自分で行う必要があります。

Android アプリを開発して Google Play で複数の APK を利用する場合は、最初から適切な方法を採用し、開発プロセスで不必要なトラブルが生じるのを避けることが重要です。このレッスンでは、アプリの APK を複数作成し、各 APK で異なるクラスの画面サイズをカバーする方法を説明します。また、複数 APK のコードベースをできるだけ効率的に管理するために必要なツールも紹介します。

複数 APK の必要性を確認する

さまざまな Android デバイスで動作するアプリを作成する場合、個々のデバイスでアプリの外観が最適になるようにしたいはずです。大画面のスペースを活用しつつ小さな画面にも対応し、最先端のデバイスで利用可能な新しい Android API 機能や視覚的テクスチャを使用しつつ、古い機能やテクスチャも放棄しないというのが理想です。最初は複数 APK をサポートすることが最善のように思えるかもしれませんが、多くの場合そうではありません。複数 APK のガイドの単一 APK を代わりに使用するセクションには、Google のサポート ライブラリの使用など、単一 APK で目的を達成する方法に関する有用な情報のほか、Android デベロッパー ガイドの各リソースへのリンクが含まれています。

アプリを単一 APK に限定できるのであれば、次のような利点があります。

  • 公開とテストがより簡単になります
  • 管理するコードベースは 1 つだけです
  • アプリはデバイス構成の変更に適応できます
  • 複数のデバイスにまたがるアプリの復元が機能します
  • 市場選好、ある APK から次の APK への「アップグレード」の動作、どの APK がどのクラスのデバイスに対応するかなどを考慮する必要がありません

この後のレッスンでは、トピックを調査し、リンク先のリソースの資料を慎重に吟味したうえで、複数 APK がアプリにとって正しい選択であると判断したという前提に立って説明を進めます。

要件を図示する

はじめに、単純な図を作成して、必要な APK の数と各 APK がカバーする画面サイズをすばやく決定します。要件の図は短時間で簡単に作成でき、後で手軽に参照できるので便利です。たとえば、API と画面サイズの 2 つのディメンションによって APK を分けたいとします。考えられる値の各ペアに対応する行と列を持つ表を作成し、いくつかのブロックごとに色を塗ります。それぞれの色は 1 つの APK を表します。

3 4 5 6 7 8 9 10 11 12 +
標準
特大

上記の図は、APK を 4 つに分ける場合の例です。青はすべての小画面 / 標準画面デバイス用、緑は大画面デバイス用、赤は特大画面デバイス用で、いずれも API の範囲は 3 から 10 です。紫は特殊なケースであり、すべての画面サイズに対応しますが、API は 11 以降のみに対応します。重要なのは、この図をひと目見るだけで、どの APK がどの API / 画面サイズの組み合わせをカバーしているかがすぐにわかることです。また、各 APK を簡潔なコードネームで呼ぶと、伝達もスムーズになります。「Xoom で赤をテストしたよね?」の方が、「Xoom で 3 から 10 対応の特大画面 APK をテストしたよね?」よりはるかに簡単です。この図をプリントアウトして、コードベースの作業に従事するすべての人に渡します。そうすれば、仕事がずっと楽になります。

ライブラリ プロジェクトにすべての共通のコードとリソースを配置する

これは、既存の Android アプリを変更する場合でもゼロから作成する場合でも、コードベースに対して最初に実施すべき非常に重要な作業です。ライブラリ プロジェクトに配置したものはすべて、更新が一度だけで済みます(言語別にローカライズされた文字列、色のテーマ、共有コードのバグの修正を考えてください)。これにより、開発時間が短縮され、簡単に回避できたはずの誤りが生じる可能性が減少します。

注: ライブラリ プロジェクトを作成してインクルードする実装の詳細はこのレッスンでは取り上げませんが、Android ライブラリの作成を参照すれば、効率的に実装できます。

既存のアプリを変換して複数 APK のサポートに使用する場合は、ローカライズされたすべての文字列ファイル、値のリスト、テーマの色、メニュー アイコン、APK 全体で変更されないレイアウトをコードベースで調べて、それらをすべてライブラリ プロジェクトに配置します。ほとんど変更されないコードもライブラリ プロジェクトに配置する必要があります。そうしたクラスを拡張して、APK ごとにメソッドを 1 つか 2 つ追加できます。

一方、アプリをゼロから作成する場合は、最初にできるだけ多くのコードをライブラリ プロジェクト内で作成し、必要なときだけ個々の APK に移動します。コードを個別に APK に追加していき、数か月経った後でどうすれば大量のコードをライブラリ セクションに移動できるかに頭を悩ませるより、上記のように長期的な視野に立って管理する方がはるかに簡単です。

新しい APK プロジェクトを作成する

リリースする APK ごとに別個の Android プロジェクトが必要です。整理しやすいように、ライブラリ プロジェクトと、すべての関連する APK プロジェクトを同じ親フォルダの下に配置します。 また、各 APK は同じパッケージ名を持つ必要がありますが、必ずしもパッケージ名をライブラリと共有する必要はありません。前述のスキームに従って 3 つの APK を使用する場合、ルート ディレクトリは次のようになります。

    alexlucas:~/code/multi-apks-root$ ls
    foo-blue
    foo-green
    foo-lib
    foo-purple
    foo-red
    

プロジェクトを作成したら、ライブラリ プロジェクトを各 APK プロジェクトへの参照として追加します。可能であれば、ライブラリ プロジェクトで開始アクティビティを定義し、APK プロジェクトでそのアクティビティを拡張します。ライブラリ プロジェクトで開始アクティビティを定義すると、アプリの初期化をすべて 1 か所にまとめることができるため、アナリティクスの初期化、ライセンス チェックの実行、各 APK でほとんど変わらないその他の初期化手続きなどの「汎用的」なタスクを個々の APK で再実装する必要がなくなります。

マニフェストを調整する

ユーザーが複数 APK を使用するアプリを Google Play でダウンロードする場合、次の 2 つの単純なルールで適切な APK が選択されます。

  • マニフェストで特定の APK が適格であることが示されている必要がある
  • 適格な APK のうち、バージョン番号が最も大きいものが優先される

例として、前述の複数 APK のセットを取り上げます。各 APK は、「ターゲット」の画面サイズより大きい画面サイズをすべてサポートするように設定されていると仮定します。前述の例の図を見てみましょう。

3 4 5 6 7 8 9 10 11 12 +
標準
特大

各 APK がカバーする範囲は重複してもかまわないので、次のように記述できます。

  • 青はすべての画面サイズをカバーし、minSDK は 3 です。
  • 緑は大画面以上の画面サイズをカバーし、minSDK は 3 です。
  • 赤は特大画面(通常はタブレット)をカバーし、minSDK は 9 です。
  • 紫はすべての画面サイズをカバーし、minSDK は 11 です。

上記のルールでは、多くの重複があります。たとえば、API 11 の特大デバイスでは、指定された 4 つの APK のいずれも実行できると考えられます。 しかし、「最大のバージョン番号が優先する」ルールにより、次のように優先順位を設定できます。

紫 ≥ 赤 ≥ 緑 ≥ 青

重複をすべて許容することには理由があります。紫の APK には、他の 3 つにはない要件があると仮定しましょう。Android デベロッパー ガイドの Google Play 上のフィルタページには、考えられる要因の完全なリストがあります。例として、紫には前面カメラが必要であるとします。つまり、紫の主要な目的は、前面カメラで撮影を楽しむことです。しかし、実のところ、すべての API 11 以上のデバイスに前面カメラが搭載されているわけではありません。その場合はどうなるでしょうか?

幸いなことに、ユーザーがそのようなデバイスで Google Play を閲覧している場合、Google Play はマニフェストを参照して、紫が前面カメラを要件として挙げていることを認識できます。紫とそのデバイスは要件が一致しないと判断され、紫は単純に無視されます。次に、Google Play は、赤が特大デバイスに対応しているだけでなく、前面カメラの有無を気にしないことを認識します。この場合、ユーザーは Google Play からアプリをダウンロードできます。デバイスに前面カメラがないのは残念ですが、特定の API レベルをサポートする APK が存在するからです。

すべての APK を別個の「トラック」で保存するには、適切なバージョン コードのスキームを用意することが重要です。デベロッパー ガイドのバージョン コード セクションに記載されているスキームの使用をおすすめします。セクション全体が参考になりますが、基本的なポイントは、APK のセットを表すコードの構成です。すなわち、2 桁で minSDK を表し、2 桁で最小 / 最大画面サイズを表し、3 桁でビルド番号を表します。これにより、デバイスが Android の新しいバージョンにアップグレードされると(たとえば 10 から 11)、現在インストールされている APK より優先順位が高い適格な APK がデバイスによって「アップグレード版」と見なされます。バージョン番号のスキームを上記の例の APK セットに適用すると、次のようになります。

青: 0304001, 0304002, 0304003...
緑: 0334001, 0334002, 0334003
赤: 0344001, 0344002, 0344003...
紫: 1104001, 1104002, 1104003...

以上をまとめると、Android マニフェストは次のようになります。

青:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        android:versionCode="0304001" android:versionName="1.0" package="com.example.foo">
        <uses-sdk android:minSdkVersion="3" />
        <supports-screens android:smallScreens="true"
            android:normalScreens="true"
            android:largeScreens="true"
            android:xlargeScreens="true" />
        ...
    

緑:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        android:versionCode="0334001" android:versionName="1.0" package="com.example.foo">
        <uses-sdk android:minSdkVersion="3" />
        <supports-screens android:smallScreens="false"
            android:normalScreens="false"
            android:largeScreens="true"
            android:xlargeScreens="true" />
        ...
    

赤:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        android:versionCode="0344001" android:versionName="1.0" package="com.example.foo">
        <uses-sdk android:minSdkVersion="3" />
        <supports-screens android:smallScreens="false"
            android:normalScreens="false"
            android:largeScreens="false"
            android:xlargeScreens="true" />
        ...
    

紫:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        android:versionCode="1104001" android:versionName="1.0" package="com.example.foo">
        <uses-sdk android:minSdkVersion="11" />
        <supports-screens android:smallScreens="true"
            android:normalScreens="true"
            android:largeScreens="true"
            android:xlargeScreens="true" />
        ...
    

技術的には、複数 APK は supports-screens タグまたは compatible-screens タグのいずれかで機能することにご注意ください。一般的には supports-screens が好まれます。同じマニフェストで両方のタグを使用することは避けてください。処理が必要以上に複雑になり、誤りが生じる可能性が増えます。 また、マニフェストでは画面サイズのデフォルト値(デフォルトでは「小」と「標準」が常に true です)を利用する代わりに、各画面サイズの値を明示的に設定してください。そうすれば、将来の悩みの種が少なくなります。たとえば、ターゲット SDK が 9 未満のマニフェストでは、「特大」はまだ存在しなかったため、自動的に false に設定されます。画面サイズは必ず明示的に設定しましょう。

リリース前チェックリストを確認する

Google Play にアップロードする前に、次の項目を念入りにチェックしてください。次に示すのは複数 APK と特に関連の深い項目であり、Google Play にアップロードするすべてのアプリ用の完全なチェックリストではないことにご注意ください。

  • すべての APK のパッケージ名は同じである必要があります。
  • APK はすべて、同じ証明書で署名する必要があります。
  • プラットフォーム バージョンで APK が重複している場合、minSdkVersion が大きい APK の方が、大きいバージョン コードを持つ必要があります。
  • APK がサポートするすべての画面サイズをマニフェストで true に設定し、サポートしないすべての画面サイズを false に設定します。
  • マニフェスト フィルタを念入りにチェックして競合する情報がないことを確認します(Cupcake バージョンの特大画面のみをサポートする APK は誰も見ることができません)。
  • 各 APK のマニフェストは、サポートされる画面、OpenGL のテクスチャ、プラットフォーム バージョンのうち、少なくとも 1 つで一意であることが必要です。
  • 1 台以上のデバイスで各 APK をテストします。それとは別に、開発マシンには、業界で最もカスタマイズしやすいデバイス エミュレータの 1 つが搭載されています。ぜひ利用してください。

また、市場に出す前にコンパイル済みの APK を検査して、アプリが Google Play に表示されるのを妨げるような不測の要因がないかを確認することも重要です。そのためには、「aapt」ツールを使用すると非常に便利です。Aapt(Android Asset Packaging Tool)は、Android アプリを作成およびパッケージ化するビルドプロセスの一部であり、アプリを手軽に検査できるツールでもあります。

    >aapt dump badging
    package: name='com.example.hello' versionCode='1' versionName='1.0'
    sdkVersion:'11'
    uses-permission:'android.permission.SEND_SMS'
    application-label:'Hello'
    application-icon-120:'res/drawable-ldpi/icon.png'
    application-icon-160:'res/drawable-mdpi/icon.png'
    application-icon-240:'res/drawable-hdpi/icon.png'
    application: label='Hello' icon='res/drawable-mdpi/icon.png'
    launchable-activity: name='com.example.hello.HelloActivity'  label='Hello' icon=''
    uses-feature:'android.hardware.telephony'
    uses-feature:'android.hardware.touchscreen'
    main
    supports-screens: 'xlarge'
    supports-any-density: 'true'
    locales: '--_--'
    densities: '120' '160' '240'
    

aapt の出力を調べる際は、supports-screens と compatible-screens の値が競合していないか、マニフェストで設定した権限の結果として意図しない「uses-feature」値が追加されていないかを確認してください。上記の例では、APK は(すべてではないにせよ)ほとんどのデバイスで表示されません。

その原因は、必要な SEND_SMS 権限の追加により、android.hardware.telephony の機能要件が暗黙的に追加されたことにあります。(すべてではないにせよ)ほとんどの特大デバイスはテレフォニー ハードウェアを搭載していないタブレットであるため、このケースでは Google Play はこの APK を除外します。ただしこれは、将来、特大画面サイズとして報告するのに十分な大きさとテレフォニー ハードウェアの両方を備えたデバイスが登場するまでのことです。

幸いにも、上記の問題は、マニフェストに次の行を追加すれば簡単に解決できます。

    <uses-feature android:name="android.hardware.telephony" android:required="false" />
    

また、android.hardware.touchscreen 要件も暗黙的に追加されます。タッチスクリーンがないデバイスの TV で APK を表示するには、マニフェストに次の行を追加する必要があります。

    <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
    

リリース前チェックリストの確認が完了したら、Google Play に APK をアップロードします。Google Play を閲覧したときにアプリが表示されるまで少し時間がかかることがあります。表示されたら、最後にもう一度チェックを行います。APK が目的のデバイスをターゲットにしていることを確認するため、該当のテストデバイスにアプリをダウンロードします。これで完了です。