Android 10対応したので個人的まとめ (RSSリーダーアプリ編)


※ 一部内容を変更して再投稿しました

はじめに: ようこそAndroid 10 & ようこそ〆切

今年の9月3日、Android 10がリリースされました!

そして、ちょうど本日、Android 10に関する日本語版blogが更新されました!


と、おめてたい一方で、APIレベル28必須の締切が迫ってきました。

2019 年のターゲット API レベル要件の拡大について

  • 2019年8月: 新しいアプリで、APIレベル28(Android 9)以上をターゲットとすることが必須になります。
  • 2019年11月: 既存のアプリのアップデートで、APIレベル28以上をターゲットとすることが必須になります。

今手持ちのAndroidアプリのAPIレベル(targetSdkVersion)は26なのですが、
せっかくAndroid 10が出たので、28をジャンプしてAPIレベル29に対応しました。
きっと2020年のオリンピックが終わったころには必須APIレベル29になってAndroidX対応必須となると思います。
手持ちのアプリはRSSリーダーアプリなのでジャンルが近ければ参考になるかもしれないです。

資料をそろえた

バージョン対応表は以下となります。

名称 バージョン targetSdkVersion
Android O 8.0 26
Android O(MR1) 8.1 27
Android P 9.0 28
Android 10(Q) 10.0 29

公式の各バージョン対応のページは以下にまとまっています。

対応したことを以下にまとめます。

http通信の許可(Android 9.0対応)

こちらはAndroid 9.0対応なのですが、デフォルトのネットワークセキュリティ構成がhttps通信のみ許可になりました。
自分のアプリは汎用RSSリーダーアプリなのでhttpの外部サイトに直接アクセスしており、
usesCleartextTraffic をtrueに指定しました。

ネットワークセキュリティ構成
https://developer.android.com/training/articles/security-config.html?hl=ja

targetSdkVersion:26 のデフォルト設定は以下のようになります。

    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>

targetSdkVersion:28 のデフォルト設定は以下のようになります。

    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>

近年はLet's Encryptの普及もありhttps通信が当たり前になっているので
通信先が決まっていてすべてhttps通信の場合は対応不要です。
一部がhttpの場合はドメイン指定 network_security_config でドメイン指定する方法もあります。
汎用的にURLにアクセスするブラウザアプリなどでは以下のように AndroidManifest.xml に平文通信使用の指定が必要になります。

AndroidManifest.xml
    <application
        android:name=".application.MainApplication"
        android:usesCleartextTraffic="true" <!-- ここを追加 -->
        :

プライバシーの変更

Android 10ではプライバシー(外部ストレージ、位置情報取得、バックグラウンドのアクティビティ起動など)周りが大きく変更になります。 自分は対応なしでしたが影響大きそうなので概要書きます。

Privacy changes in Android 10
https://developer.android.com/about/versions/10/privacy/changes#scoped-storage

外部ストレージ対応

Manage scoped external storage access

本対応は、今後targetSdkVersionに関わらずすべてのアプリで対応する必要があります。

Android 10(APIレベル29)以上を対象とするアプリには、デフォルトで外部ストレージデバイスまたはスコープストレージへのスコープアクセスが許可されます。
以下のAPIを使用している方は今後使用できなくなるようなので注意が必要です。

推奨対応として、独自データを扱う場合には Context#getExternalFilesDir()を、一般的なメディアを取り扱う場合は MediaStore 、一般的なファイルを扱う場合は Storage Access Framework を使用します。

一時的オプトアウト(不参加)の方法として android:requestLegacyExternalStorage="true" を指定する方法がありますがアンインストールまで設定が引き継がれます。

折りたたみ端末対応

動作確認方法

Galaxy Fold
https://www.galaxymobile.jp/galaxy-fold/

2019年9月現在はGalaxy Foldしか存在しないですがAndroid 10では折りたたみ端末に対応しています。
Android Studio 3.5ではデフォルトで7.3インチと8.0インチの仮想折リたたみ端末(Foldable)が用意されています。

上記でエミュレータ実行を行うと右下のボタンで折りたたみが行える様になり、動作確認を行うことができます。


Android 7.0のマルチウィンドウのサポートの拡張版という印象です。
すでにChromebook用などにresizableActivity対応していたら対応が容易だと思います。
Android 8.0対応時に、縦長端末対応のために android:maxAspectRatio に対応していましたが
今回は折りたたみで縦長から横長になったときも考慮して android:minAspectRatio も設定しました。

マルチウィンドウについては後述します。

マルチウィンドウ対応

折りたたみ式デバイスに対応したアプリの作成
https://developer.android.com/preview/features/foldables?hl=ja

マルチウィンドウ対応大きく変更になったのは複数起動している時のライフサイクルです。
Android 10上でアプリが稼働する場合の、 onResume() メソッドと onPause() の通知タイミングが異なります。
アプリを複数起動している場合、再開時にフォーカスしているアプリのみでなくすべてのアクティビティが再開状態になるようです。

このため、新しいライフサイクル onTopResumedActivityChanged(boolean) が追加され優先度が最高のアプリを区別できます。

自分のアプリではカメラなどを扱っていないので動作確認のみだったのですが、カメラ・メディア・マイクなど使用しているアプリは注意する必要がありそうです。

ダークテーマのAndroid 10対応

過去にダークモードのあれこれを書いたのですが、今回Android 10ではコントロールセンターからダークモード切り替えができるようになりました。

これによりダークモードをシステムに従うMODE_NIGHT_FOLLOW_SYSTEM や、バッテリー状態によって切り替える MODE_NIGHT_AUTO_BATTERY を使用するのが良さそうです。
Material Component v1.1.0-alpha03では時間による切り替えは非推奨になりました。

MODE_NIGHT_NO. Always use the day (light) theme.
MODE_NIGHT_YES. Always use the night (dark) theme.
MODE_NIGHT_FOLLOW_SYSTEM (default).
MODE_NIGHT_AUTO_BATTERY. ✨New in v1.1.0-alpha03.
MODE_NIGHT_AUTO_TIME & MODE_NIGHT_AUTO. ⛔ Deprecated in v1.1.0-alpha03.

MODE_NIGHT_FOLLOW_SYSTEM に設定しコントロールパネルから切り替えた場合、 Activity#recreate() は不要でよろしくダークモード切り替えを行ってくれました。
また、ダークモードに対応する余裕がない方向け(?)に android:forceDarkAllowed="true" で強制的にダークモードに対応する機能も提供になりました。

ジェスチャーナビゲーション

Android 10エミュレータで、初期設定の下部ナビゲーションバーは見慣れた3-buttonだったのですが、 System > Gesture で3種類の中から選べます。
Android 10ではジェスチャーナビゲーション推しのようです

対応には大きく「全画面対応」「スワイプ処理のコンフリクト対応」が必要となります。
それぞれは以下の公式Mediumで触れられています。

Gesture Navigation: Going edge-to-edge
https://medium.com/androiddevelopers/gesture-navigation-going-edge-to-edge-812f62e4e83e

Gesture Navigation: Handling visual overlaps
https://medium.com/androiddevelopers/gesture-navigation-handling-visual-overlaps-4aed565c134c

[断念] 全画面対応

結論から言うと自分は全画面対応を諦めました。
対応方法は以下の動画で詳しく説明していますが対応しなかった理由(言い訳)が二つあります。

droidcon NYC 2017 - Becoming a master window fitter🔧
https://youtu.be/_mGDMVRO3iE

ひとつめは上記の自分のアプリは DrawerLayoutCoordinatorLayout で構成しており、上記キャプチャの通り対応箇所が多い点です。
ふたつめは安定版最新のAndroidXライブラリ androidx.core:core:1.1.0 にInsetsを便利に扱うクラス WindowInsetsCompat がまだはいっていなかった点です。

というわけでいったん全画面は見送りました。

↓未対応だと画面上と画面下が塗りつぶされてしまうのでこれを透明にする必要があるのですが
あまりに気にならないようにも見えるので..

スワイプ処理のコンフリクト対応

Android 10ではジェスチャーナビゲーションの場合、画面横端のスワイプが 戻る処理 に、画面下端のスワイプが ホーム処理 になります。

Dark Theme & Gesture Navigation (Google I/O'19)
https://youtu.be/OCHEjeLC_UY

上記の動画を見ればスワイプ処理への影響がわかります。
基本的にはAndroidXの最新コンポーネントを使用すればNavigationDrawerなどの処理はよろしくやってくれるそうです。

必須対応として、画面下にあるコンポーネントと衝突しないようにする必要があります。
自分は上記の全画面対応を断念したこともあり、対応不要でした。(動画32:00頃)

また、カルーセルなどの横スクロール処理でスワイプ処理を戻るに持っていかれてしまうのを避けたいのですが、
エッジの除外処理はしないことを推奨とのことです。(動画39:00頃)
若干不安ですが従うことにしました。

Robolectricのテストがコケる件

targetSdkVerison:29にすると、最新のRobolectric4.3で以下のエラーが出ました。

java.lang.IllegalArgumentException: API level 29 is not available

リリースノートを見るとAndroid 10にはJava9が必須要件のようです。
https://github.com/robolectric/robolectric/releases

Robolectric 4.3
:
Android SDK 28 (includes support for testing against SDKs from 16 on) and Android Q Beta 2.
Running tests on Android Q requires Java 9.
Legacy resources mode will not be supported for Android Q.

また、targetSdkVersion:28以上で動作させると
Looper周りの挙動が変わり、今までのテストが動作しなくなる恐れがあります。

自分の場合は新しいLooperModeに対応するため、対象クラスに @LooperMode(PAUSED) アノテーションを付与しました。
また、 Java9対応はしんどいので、 src/test/resource/robolectric.properties でSDK指定を行い28で動作させることにしました。

robolectric.properties
# sdk29 required Java9
sdk = 28

Looper周りの挙動を変えたくない場合は
Robolectric 4.2以下を使用するかsdkを27以下に指定すると今までと同じ挙動になります。

robolectric.properties
# sdk29 required Java9
# sdk28 required @LooperMode(PAUSED)
sdk = 27

詳細については下記Robolectric公式サイトページに載っています。

Improving Robolectric's Looper simulation
http://robolectric.org/blog/2019/06/04/paused-looper/

感想

今回の対応は、ジェスチャーナビゲーション対応があまり変化ないように見えますが対応がけっこうありそうです。
そもそもジェスチャーナビゲーションありきでアプリを作ると画面の再設計が必要になりそうです。

昔のAndroid端末には画面下にいろんなボタンとくるくる回せるボールがついていて、それはそれは便利だったんじゃよ。

おめでとうAndroid 10!
おわり。