Androidでアプリ開発をする際に、minSdkVersionとtargetSdkVersionの関係性がいまいち理解できていなかったので、
実際にコード書いて検証してみた話。
まとめ
- 下位互換性を保証するためにminSdkVersionを指定する場合は、minSdkVersion以降で追加されたAPIはそのまま使ってはならない
- minSdkVersionを指定しつつも、より最新の機種では新しいAPIを使いたい場合は、実行時のAPIレベルを見て処理を分ける
- (Support Libraryが使えそうであれば使う)
準備
build.gradle
- コンパイルはAPIレベル22で行う
- 最低動作保証APIレベルは15とする
build.gradle
minSdkVersion 15
targetSdkVersion 22
検証コード
以下のようなNotificationを実装する。
MainActivity.java
Notification notification = new Notification.Builder(context)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("test title")
.setContentText("test content")
.setContentIntent(pendingIntent)
.setColor(Color.RED) // API level 21 から追加されたAPI
.build(); // API level 16 から追加されたAPI
Androidエミュレータ
以下のバージョンを準備。
- Android 4.0.3 (API level 15)
- Android 5.1 (API level 22)
1.検証
1) まずは何も考えずに実装してみる
最低動作保証APIレベルを15と指定しているのにもかかわらず、
APIレベル16以降で追加されたメソッドを実行しようとしているので、
APIレベル16以降から追加されたメソッド部分で警告が出る。
ただしコンパイルはAPIレベル22で行われるので、apk自体は作成できる。
このコードは、Android 4.0.3のエミュレータでは動かない。
(当該APIが存在しないため)
08-08 10:29:09.807 1558-1558/org.tksmaru.sandbox.android.sdkversion E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.NoSuchMethodError: android.app.Notification$Builder.setColor
at org.tksmaru.sandbox.android.sdkversion.MainActivity$1.onClick(MainActivity.java:38)
at android.view.View.performClick(View.java:4084)
at android.view.View$PerformClick.run(View.java:16966)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Android 5.1では動く。
2) 動作保証したい全てバージョンで動かすには
minSdkVersion以降で追加されたAPIは使わない
MainActivity.java
Notification notification = new Notification.Builder(context)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("test title")
.setContentText("test content")
.setContentIntent(pendingIntent)
.getNotification(); // API level 16 以降はdeprecatedだが、22時点では動く
実行時のAPIレベルごとに処理を分ける
特定の実行時APIレベル以上のときだけ新規追加されたAPIを利用できるようにする。
このように条件分岐すると、コンパイル時の警告も出なくなる。
ただし、コードが特定のAPIレベルに依存することになるので、メンテナンス性が良いとは言いづらい。
MainActivity.java
Notification notification;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// API level 21 以上
notification = new Notification.Builder(context)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("test title")
.setContentText("content is this")
.setContentIntent(pendingIntent)
.setColor(Color.RED) // API level 21 から追加
.build(); // API level 16 から追加
} else {
// API level 20 以下
notification = new Notification.Builder(context)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("test title")
.setContentText("content is this")
.setContentIntent(pendingIntent)
.getNotification();
}
Support Libraryを使う
(まだ調べてない)
まとめ
- 下位互換性を保証するためにminSdkVersionを指定する場合は、minSdkVersion以降で追加されたAPIはそのまま使ってはならない
- minSdkVersionを指定しつつも、より最新の機種では新しいAPIを使いたい場合は、実行時のAPIレベルを見て処理を分ける
- (Support Libraryが使えそうであれば使う)
参考