Android Kotlin基礎講座 01.3: イメージリソースと互換性

タスク:APIレベルと互換性について理解する

Android開発の優れた点のひとつとして挙げられるのが、あなたが開発したコードを実行することができる端末の多さです。というのもスマホでいえばNexus OneからPixelまでであったり、また端末の形状もタブレットからノートパソコン、腕時計、テレビ、車など幅広く対応しています。

Android開発用のコードを書く際に、異なる端末用のアプリのために完全に別々のコードを書く必要はありません。たとえ腕時計型端末やテレビなどといった根本的に形が違う端末で動くアプリであったとしてもです。しかし多くの端末に対応するためには理解しておかなければならない制約と互換性の対策がいくつかあります。

このタスクでは特定のAndroid APIレベル(バージョン)を対象とする方法と、古い端末(以前のバージョン)に対応するために必要なAndroid Jetpackライブラリーの使い方について学習します。

ステップ1:APIレベルについて

前の記事で、プロジェクトを作成した際に、開発するアプリが対応する特定のAndroid APIレベルを指定しました。Android OSには頭文字がアルファベットの順番になっているスイーツの名前が付けられたバージョンがあります。それぞれのバージョンは新しい特色と機能を備え付けています。例えばAndroid Oreoというバージョンでは、ピクチャーインピクチャ―に対応し、Android Pieではスライスに対応しました。APIレベルはAndroidバージョンに準じています。例えばAPI 19はAndroid 4.4(KitKat)に準じています。

ハードウェアがサポートできるもの、ユーザーが端末の更新をするかどうか、メーカーが異なるOSレベルをサポートするかなど、多くの要因によりユーザーは必然的に様々なバージョンの端末を利用するようになります。

プロジェクトを作成した際にアプリがサポートする特定の最小APIレベルを指定しました。すなわち、アプリがサポートする最も古いAndroidバージョンを指定したということです。またアプリにはアプリがコンパイルされるレベルとアプリが対象とするレベルがあります。これらのレベルはGradleビルドファイルの環境設定パラメータです。

  1. Gradle Scriptsフォルダーを展開して、build.gradle(Module: app)ファイルを開いてください。

    このファイルは特定のアプリのモジュールでのビルドパラメーターと依存関係を定義しています。build.gradle(Project: DiceRoller)ファイルはプロジェクト全体のビルドパラメーターを定義しています。
    多くの場合、アプリのモジュールはプロジェクト内唯一のモジュールなので、この分割は任意のものとなります。しかしアプリが更に複雑になり、アプリをいくつかの部分に分割した場合や、アプリがAndroid watchのようなプラットフォームをサポートしている場合は、同じプロジェクトの中に異なるモジュールがあることがあります。
  2. build.gradleファイルの一番上にあるandroidセクションの中を確認してください。(下のサンプルコードはセクション全体ではありませんが、この記事でもっとも重要と思われる箇所を含んでいます)
android {
   compileSdkVersion 28
   defaultConfig {
       applicationId "com.example.android.diceroller"
       minSdkVersion 19
       targetSdkVersion 28
       versionCode 1
       versionName "1.0"
   }
  1. compileSdkVersionというパラメータ―を見てください。
compileSdkVersion 28

このパラメーターはGradleがアプリをコンパイルするために使うべきAndroid APIレベルを定めています。これはアプリがサポートできる最も新しいバージョンになります。つまり、開発するアプリはこのAPIレベルとこれ以下のレベルに含まれたAPIの機能を使うことができるということです。今回の場合はアプリはAPI 28をサポートしており、それはAndroid 9(Pie)にあたります。

  1. targetSdkVersionパラメーターを見てください。defaultConfigセクションの中にあります。
targetSdkVersion 28

この値はアプリをテストした最も最近のAPIになります。多くの場合、この値はcompileSdkVersionと同じ値になります。

  1. 続いてminSdkVersionパラメーターを見てください。
minSdkVersion 19

このパラメーターは三つの中で最も重要です。これはアプリが起動する最も古いAndroidのバージョンを決めています。ここで定義されているAPIレベルより古いAndroid OSを備えている端末ではアプリは一切動作しません。

アプリの最小APIレベルを選ぶのは大変なことかもしれません。最小APIレベルを低くしすぎるとAndroid OSの最新の機能を使えないことになりますし、高くしすぎると限られた端末でしか動作しないことになります。

プロジェクトのセットアップでアプリの最小APIレベルを設定する画面にきたら、Help me chooseをクリックするとAPI Version Distributionダイアログが表示されます。
このダイアログではどのくらいの端末が異なるOSレベルを使っているのか、OSレベルで追加された機能や変更された機能などの情報を提示しています。また、異なるAPIレベルをサポートすることに関するより詳細な情報は、Androidドキュメンテーションリリースノートやダッシュボードから確認することもできます。

ステップ2:互換性について

異なるAndroid APIレベルのためにコードを書くことはアプリ開発者が直面する共通の課題です。ですのでAndroidフレームワークチームはそのための多くの手助けを用意してくれています。

2011年にチームは下位互換クラスと有用な機能を提供する初めてのサポートライブラリである、Google-developed ライブラリをリリースしました。2018年には、Google社はAndroid Jetpackライブラリを発表しています。これはそれ以前のサポートライブラリのクラスや機能の多くを含んだコレクションです。

  1. MainActivityを開いてください。
  2. MainActivityクラスはActivityクラスではなく、AppCompatActivityクラスを継承していることを確認してください。
class MainActivity : AppCompatActivity() { 
...

AppCompatActivityは異なるプラットフォームのOSレベルでも同じく扱えるようにするための互換性クラスです。

  1. importから始まる行の隣にある+マークをクリックして、import文を展開してください。AppCompatActivityクラスがandroidx.appcompat.appパッケージからインポートされたクラスであることを確認してください。Android Jetpackライブラリの名前空間はandroidxです。
  2. bulild.radle(Module: app)を開いて、dependenciesセクションまでスクロールしてください。
dependencies {
   implementation fileTree(dir: 'libs', include: ['*.jar'])
   implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
   implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
   implementation 'androidx.core:core-ktx:1.0.1'
   implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
   testImplementation 'junit:junit:4.12'
   androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
   androidTestImplementation 
        'androidx.test.espresso:espresso-core:3.1.0-alpha4'
}

appcompatライブラリがandroidxの一部で、AppCompatActivityクラスを含んでいることを確認してください。

Tip: 一般的に開発するアプリがJetpackライブラリからの互換性クラスを使用できる場合、アプリはJetpackライブラリのクラスのうちの一つを使うべきです。なぜならそれらのクラスは最大限可能な数の機能と端末をサポートしているからです。

ステップ3:ヴェクター画像に互換性を追加する

これからアプリの最終調整をするために、先ほど学んだ名前空間、Gradle、互換性についての新たな知識を活用していきます。アプリのサイズを古いプラットフォームに最適化していきます。

  1. resフォルダーを展開して、drawableを展開してください。サイコロ画像のひとつをダブルクリックしてください。

    先ほど学習したように、全てのサイコロ画像は実際には色や形を定義したXMLファイルです。こういったファイルはヴェクター画像と呼ばれます。ヴェクター画像のPNGなどのようなビットマップ画像に対する利点は画質を下げずにサイズを変更できることです。さらに、ヴェクター画像は通常ビットマップ画像よりもファイルサイズも小さいです。

    ヴェクター画像に関して触れなければならない重要なことは、ヴェクター画像はAPI 21以降から対応されているということです。しかし今作っているアプリの最小SDKはAPI 19に設定されています。ですが、仮にAPI19を搭載した端末やエミュレーターでアプリを動作させようとした場合、通常通りアプリが起動することがわかります。どうしてでしょうか。

    アプリをビルドする際、Gradleビルドの過程でヴェクターファイルからはPNGファイルが生成され、それらのPNGファイルはAPI21未満の端末で使われます。しかしこれらの余分なPNGファイルはアプリのサイズを増加させます。不必要に大きいアプリは良いアプリとは言えません。それによってユーザーのダウンロードにかかる時間も長くなりますし、端末の限られた容量も圧迫してしまいます。また、大きいサイズのアプリはアンインストールされる可能性が高くなります。ダウンロードの失敗やキャンセルにもつながりかねません。

    ここで朗報があります。APIレベル7にまで遡って、ヴェクター画像のAndroid X互換性ライブラリが存在するということです。
  2. build.gradle(Module: app)を開いてください。defaultConfigセクションに次のコードを追加してください。
vectorDrawables.useSupportLibrary = true
  1. Sync Nowボタンをクリックしてください。build.gradleファイルが変更される度にビルドファイルをプロジェクトと同期する必要があります。
  2. activity_main.xmlレイアウトファイルを開いてください。<LinearLayout>タグの中のtools名前空間の下に次の名前空間を追加してください。
xmlns:app="http://schemas.android.com/apk/res-auto"

appという名前空間は自身でカスタムしたコード、またはライブラリ由来の属性のための名前空間で、核となるAndroidフレームワークのものではありません。

  1. <ImageView>要素のandroid:src属性をapp:srcCompatに変更してください。
app:srcCompat="@drawable/empty_dice"

app:srcCompat属性は古いAndroidバージョンにもヴェクター画像を対応させるためのAndroid Xライブラリを使用しており、API 7まで対応させることができます。

  1. アプリをビルド、起動してください。画面上に違いはありませんが、アプリは端末のバージョンに関係なくサイコロ画像のPNGファイルを生成する必要がなくなりました。これはアプリのファイルサイズが小さくなることを意味しています。

完成品

今回の記事の最終的な完成コードを含むプロジェクトはこちらからダウンロードできます。⇒DiceRollerFinal