Marshmallowでアプリをテストするときのこと
片渕 真太郎
スクラムエンジニアリンググループの片渕です。
Google I/O 2015でAndroid M Developer Preview(Marshmallow)が発表されてから数ヶ月が経ちました。
新型のNexus5が発表されるという情報もあり、Marshmallowがユーザーの手に届く日は遠くなさそうです。
開発者としては、新機能にワクワクする一方でアプリが不具合を出したりクラッシュしないかというのが最も気にかかる所だと思います。
という事で今回は、私が担当しているアプリをMarshmallowで動作確認した際の知見を共有できればと思います。
新OSへの対応というのは通常、deprecatedになったAPIを消したり新規に追加されたAPIを呼ぶ様に修正するというのが正規のアプローチですが、現実問題としてプロダクトの状況によってはそこまでパワーを割けない場合もあります。そうした場合に「とりあえず」凌ぐ方法も紹介しています。
前提として、Android DevelopersにTesting Guideが公開されていますので、正確で詳細な情報を知りたい方はこちらを参照してください。
また、本情報は2015/09/27時点で最新のSDK preview3の仕様に基づくものです。
Permissions
今回最もドラスティックな変更として注目を集めているRuntime Permissionsですが、Google Playからインストール時に一括でパーミッションの許可を求める方式から、アプリ実行時に許可を求める方式へと変更になりました。
詳細について知りたい方はAndroid Developersか、日本語の資料だと以下のスライドに詳しくまとまっています。
Marshmallowではアプリの設定画面からいつでもパーミッションをオフにする事ができる為、パーミッションを使う処理を正しく制御しないとクラッシュの原因に成り得ます(公式にはthe method returns an empty data set
と書いてありますが、実際に実行時例外を吐くような物もありました)。
targetSdkVersion
が23を下回っている場合も、Google Playからインストール時に一括で権限を確認する挙動は従来と変わらないものの、設定画面から権限をオフにできてしまうので無関係というわけにはいきません。
テスト
- 自分のアプリが対応が必要なパーミッションを利用しているか確認する
- Android developersの
Permissions and permission groups
という表にまとまっています。 - 拙作のm-permissions-checkerを利用する事で検査を自動化できます。
- 上記以外にも、protectionLevelがdangerousなカスタムパーミッションに関しては対象となるようです。
- Android developersの
- 以下のadbコマンドを用いるか、設定画面から権限をオフにした時にアプリの挙動がおかしくならないか確認する
1 |
$ adb shell pm [grant|revoke] <package.name> <permission.name> |
対応
Must
- support v4 libraryのバージョンを23.0.0以上にアップデートする
- CompatやPermissionChecker等対応するクラスを追加する為です。
- 対象の処理をパーミッションをチェックするコードで囲む
- 最小限な対応としては、処理を呼び出す前に PermissionChecker.checkSelfPermission でチェックして権限がオフの場合はToastで警告を出すという簡易なものでも良いと思います。
Option
- targetSdkVersionを23にする
- 対象の処理をパーミッションをチェックするコードで囲む
- ハンドリングするコードを書く場合は、以下を参考にしてください。例はAcitivtyの想定ですが、Fragmentの場合は微妙に違うので適宜読み替えてください。より詳細な実装はgooglesamples/android-RuntimePermissionsを参考にしてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// パーミッションをチェック if (PermissionChecker.checkSelfPermissions(this, PERMISSION_SHOWCAMERA)) { showCamera(); } else { // rationaleを出すべきかチェック if (ActivityCompat.shouldShowRequestPermissionRationale(this, PERMISSION_SHOWCAMERA)) { showRationaleForCamera(); } // パーミッションを要求 // Compatを利用するとtargetSdkVersionが23より小さくてもDialogが表出する ActivityCompat.requestPermissions(target, PERMISSION_SHOWCAMERA, REQUEST_SHOWCAMERA); } // コールバック @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case REQUEST_SHOWCAMERA: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 許可された場合 showCamera(); } else { // 拒否された場合 deniedActionForCamera(); } break; default: break; } } |
ちなみに、上記の様なボイラープレートを書くのは非常に面倒なので自動生成するライブラリを用意しました。
ぜひご検討ください。
Doze and App Standby
バッテリーの延命策として、DozeというシステムのスリープモードとApp Standbyというアプリのスリープモードがそれぞれ追加されました。
明記はされていませんが、端末やアプリを一定期間触っていないとそれぞれの状態に突入する様です。
Doze
以下のadbコマンドで端末をDozeモードにする事ができます。
1 2 3 |
$ adb shell dumpsys battery unplug $ adb shell dumpsys deviceidle step $ adb shell dumpsys deviceidle -h |
端末がDozeモードを抜けた際に、アプリが問題なく動作するかを確認します。
Android Developersには以下の挙動が制限されると書いてあるので、特に以下の観点に注意しましょう。
- ネットワークアクセスの遮断(GCMのhigh priorityを受信した場合を除く)
- PowerManager.WakeLockは無視される
- AlarmManagerのset、setExactは保留される
- 対応したい場合は
setAndAllowWhileIdle
、setExactAndAllowWhileIdle
を使う
- 対応したい場合は
- Wi-fiスキャンは実行されない
- SyncAdapterの同期やJobSchedularのタスクは保留される
- Dozeモードを抜けた際に一気に実行される様です。
実際に私が担当しているアプリでは、NotificationをAlarmManagerで設定している箇所があったのでMarshmallowの際は該当するメソッドを呼ぶような修正を加えました。
App Standby
以下のadbコマンドでアプリをstandbyモードから開放される過程を再現できます。
1 2 3 |
$ adb shell dumpsys battery unplug $ adb shell am set-inactive <packageName> true $ adb shell am set-inactive <packageName> false |
Dozeモードと同様に、アプリが問題なく動作するかをチェックします。
特にNotificationやバックグラウンドのジョブがこれまで通り動作するかに注意します。
Auto Backup
Auto Backupとは、アプリのデータをGoogle Driveに暗号化してバックアップしてくれる機能です。
2.2より追加されていたBackup Serviceとは違い、特別な実装をしなくても全アプリが対象となります。
Auto Backupでは、以下のケースを除いて全ての永続化したデータがバックアップ対象になります。
getCacheDir()
メソッドとgetCodeCacheDir()
メソッドに参照されるディレクトリ内のファイルgetExternalFilesDir()
メソッドに参照されるディレクトリ内以外の外部ストレージ内のファイルgetNoBackupFilesDir()
メソッドに参照されるディレクトリ内のファイル
サービスの認証情報やアクセストークン等、複数端末で利用されると都合が悪いものが存在するかを確認してください。
Android Developersには、既知の問題としてGCMのregistration IDをバックアップ対象としない様に注意書きがあります。
テスト
以下のadbコマンドでバックアップを実行する事ができます。
1 2 3 4 5 6 7 |
# ログの有効化 $ adb shell setprop log.tag.BackupXmlParserLogging VERBOSE # バックアップ マネージャーを初期化 $ adb shell bmgr run $ adb shell bmgr fullbackup <PACKAGE> # 復元 $ adb shell bmgr restore <PACKAGE> |
対応
- targetSdkVersionを23にあげる
android:fullBackupContent
属性を指定する- バックアップの除外対象にしたいファイルをxmlで指定できます。より詳細な実装はgooglesamples/android-AutoBackupForAppsを参考にしてください。
1 2 3 4 |
<full-backup-content> <include domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" /> <exclude domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" /> </full-backup-content> |
android:fullBackupContent
はtargetSdkVersionが23以上でないと使えないので、23にはまだできないけど不具合は出したくない、という場合はandorid:allowBackUp属性
をfalseにしておくのが無難そうです。
私がテストをしていた際は、復元直後の起動時のみアプリで定義したカスタムApplicationクラスが呼ばれず、アプリがクラッシュするというバグに遭遇しissueを上げたのですが、別issueにて解決が報告されていました。
TIPS: Apache HTTP Client
SDKのバージョンを23に上げようとした時にぶつかる最大の問題が、Apache HTTP ClientのFrameworkからの削除です。
22でdeprecatedになったのも束の間、23ではその存在自体が消滅しています。
その為、アプリ内でApache Clientを利用している箇所は全てコンパイルエラーになります。
build.gradle
に以下の記述を追加すればとりあえずは動きますが、早めの移行を検討した方が良いでしょう。
1 2 3 |
android { useLibrary 'org.apache.http.legacy' } |
HTTP及びWeb API Clientの選定は以前として難しい状況にあり、以下のスライドを参考にしながら決めていくのが良さそうです。
Android標準のHTTP ClientはHttpUrlConnection、Web API ClientはVolleyがAOSPにバンドルされていますが、HttpUrlConnectionは実装を委譲しているOkHttpのキャッシュにバグがある事、Volleyは諸々の問題やマイルストーンが不透明な事を踏まえると、OkHttpとRetrofitの採用が良さそうだと感じています。
特にRetrofitは2.0のリリースが予定されており、Callアダプタの追加やリクエストキャンセルの実装、レスポンスオブジェクトの改善が行われている為大いに期待できそうです。
最後に
今回は変更の中でも特に影響度の高い部分に関してテストした知見を共有させて頂きました。
より詳細な変更はBehavior Changesを参照してください。
また、Direct Shareや指紋認証等新機能の対応に関しては触れませんでしたが、気になる方はAPI overviewとSamplesを覗いてみてください。