はじめに

2015年8月18日、それまで「MNC」とされていた Android次期バージョンが、「Android 6.0 Marshmallow」となることが正式に発表され、同時に正式版のSDKとAndroid 6.0 のPreview3 が公開されました。 正式版SDKでビルドしたapkはGooglePlayStoreへのサブミットが可能であり、Android 6.0 の Preview3 は実質的なGM版となるため、 Android 6.0 対応の環境が整ったと言えます。

既存アプリも対応が必要

前提としてアプリの挙動は、ビルドした際の targetSdkVersion によって決まります。 従来は、新しいバージョンのAndroid OSがリリースされた際も、既にストアに公開済のapkを動作させる分には下位互換が保たれるようになっていました。

しかし Android 6.0 で導入される新しいパーミッションモデルに伴い、ユーザがインストール後にアプリのパーミッションを取り消すことが出来るようになっており、これが既存のアプリに対しても行なえてしまいます。

既存のアプリは「パーミッションの許可状況を確認する」方法がないため、パーミッションを取り消されたアプリでは、正しい動作が保証できません。

よって今回は、既存のアプリについても新しいAPIに対応し、新しいSDKでビルドし直して公開する必要があります。

今回はAndroid 6.0 での変更のうち、既存アプリへの影響が大きいと思われる以下の2つについてまとめました。

  1. Runtime Permisson:実行時のパーミッション確認
  2. Power-Saving Optimization:省電力モードへの対応

Runtime Permissonに対応する

概要と影響

従来は、インストール時に必要なパーミッション一式を許可する Install Time Permission を採用していたAndroidですが、Android 6.0 からはアプリの実行時に 1つずつ確認する Runtime Permission へ変更になりました。

よって目的の操作に特定のパーミッションが必要な場合、その許可状況の確認と必要に応じて許可の要求を行う必要があります。

進め方

  1. AndroidManifest の uses-permission ノードで従来要求していたパーミッションと、そのパーミッションが必要な処理をしている箇所を整理する
  2. 例えば、android.permission.WAKE_LOCK を要求しているのならば、PowerManager を使用している箇所があるはずです。
  3. 必要なパーミッションとそれが属するパーミッショングループを整理する
  4. パーミッションが必要な処理を行っている箇所に、許可状況のチェック処理と未許可時の許可要求の処理を追加する

パーミッションの許可状況の確認及び要求は、個々のパーミッション単位ではなくパーミッショングループ単位で行います。 パーミッションと属するパーミッショングループの対応関係については、公式のドキュメントを参照して下さい。

許可されているかの確認

Context#checkSelfPermission メソッドで確認します。 引数でパーミッショングループ名を指定し、戻り値が PERMISSION_GRANTED か否かで判断します。

許可を求めるダイアログを出す

Activity#requestPermissions で必要なパーミッションの許可要求を行います。 引数では、複数のパーミッショングループが一気に指定できるようになっていますが、ユーザへのダイアログ表示は1つずつ順に行われます。

ユーザの許可結果は、 Activity#onPermissionRequestResult メソッドにコールバックされます。 複数のパーミッショングループについてまとめて許可を要求していた際も、コールバックはまとめて1回叩かれます。 引数の permissions と grantResults のインデックスが対応する形になっているので、「どのパーミッションが許可されて、どのパーミッションが不許可となったか?」の判断が可能になります。

正式版SDKでの変更箇所

Preview2以前から対応をしていた場合は、Preview2→Preview3でパーミッショングループが再編されていることに注意して下さい。


Power-Saving Optimization への対応

概要と影響

Android 6.0 では消費電力を抑えるために、端末の状態である Doze モードとアプリごとの状態である App Standby モードが導入されました。 これらは大雑把に言うと、OS が「アクティブに使っていなさそうなので、処理をサボろう」と判断することで各種処理が行われないというものになります。 また、 Dozeモード/App Sandbyモードの影響を受けないようにホワイトリストに追加することもできるため合わせてご紹介します。

Doze モード

Doze モードは、端末の状態です(アプリ単位ではなく、端末全体の状態)。

  • 電源に接続されていない
  • 一定時間 画面をOFF
  • 一定時間 無操作

上記の 3 つを満たした端末は、Dozeモードに移行します。

Dozeモード中の端末は、以下の制限が掛かります。

  • AlarmManager で登録したスケジュールが発火されなくなる
  • GCMは優先度:高のものしか受け取れなくなる
  • ネットワーク接続が不可になる。
  • PowerManager による画面ONが不可になる

以下にそれぞれの対策を記載します。

AlarmManager への登録

従来の AlarmManager#set メソッドや AlarmManager#setExact メソッドで登録したスケジュールは、Doze状態中は発火されないようになります。 代わりに、API Level 23で追加された AlarmManager#setAndAllowWhileIdle メソッドや AlarmManager#setExactAndAllowWhileIdle メソッドを使って登録することで、Doze状態中も発火されるスケジュール登録が出来ます。

GCM の優先度

GCM の送信パラメータの priority を “high”にすることで、優先度:高とすることができます。 詳しくは、GCM の仕様を参照してください

ネットワーク接続と PowerManagerでの操作

これらは回避法はないようです。 失敗もする前提で設計をする他ありません。

App Standby モード

「しばらく使っていないアプリ(期間の判断基準は不明)」と判断されたアプリは、App Standby モードに移行します。 こうなると、ネットワーク接続の回数に制限が掛かってしまいます。

影響として、「バックグラウンドの Service でネットワーク通信」しているものが、動かなくなる可能性があります。

詳しくはAndroid Developerをご覧ください

ホワイトリストへの追加要求

本来的には、Dozeモードや App Standbyモード下でも正しく動作するようにアプリの設計を行うべきですが、既存アプリの場合はそれが難しいことも多々あります。 Android 6.0では、ユーザは設定画面から指定したアプリが Dozeモード/App Sandbyモードの影響を受けないように設定することができます。 アプリは「自身が 省電力モードの影響を受けない設定になっているか否か?」の判断が可能で、「指定したアプリの 省電力設定画面に遷移する」方法もあります。 なので、「アプリが省電力モードの影響を受けない設定になっていない場合は、設定するよう促す」という方法も検討することができます。 当該アプリが 省電力モードの影響を受けない設定になっているか否かは、 PowerManager#isIgnoringBatteryOptimizations(String packageName) で判断できます。 指定したアプリの省電力モード無視設定に遷移させたい場合は、事前に android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS パーミッションを要求した上で、 以下のコードで startActivity を行います。

Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:test.myapplication")); // ここにパッケージ名を指定
startActivity(intent);

おわりに

今回はAndroid M で変わる仕様の中でも影響度の大きなものへの対応を取り上げましたが、 他にも新機能やdeprecatedになったものがあり、今までの概念とは異なる部分が多々見受けられます。 今回取り上げなかった仕様変更についても今後、別の記事にまとめていきたいと思います。