新規アプリは2018年8月以降
既存アプリのアップデートは2018年11月以降
targetSdkVersion を >= 26 にしないといけない話
Permissions
Runtime permissions : ユーザーは設定からon/offできるSpecial permissions : 画面の上にdrawするやつとか
Alarms
WorkManager を使うBroadcastReceivers
Android Manifest で register したほとんどの implicit receiver はもはや受け取れなくなる例外もある、ACTION_BOOT_COMPLETED とか
BroadcastReceiver の使用を避ける例として、JobScheduler を使ってネットワーク状態の変更を検知し、
val jobScheduler = getSystemService(Context.JOB_SCHDULER_SERVICE) as JobScheduler
val jobInfo = JobInfoBuilder(JOB_ID, serviceComponent)
.setRequiredNetwork(JobInfo.NETWORK_TYPE_ANY)
.build()
BroadcastReceiver は Android Manifest で disabled にしておき(android:enabled="false" を追加)
<receiver
android:name=".NetworkConnectionReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
JobScheduler で receiver を有効にする
fun setNetworkReceiverState(enabled:Boolean) {
val componentName = ComponentName(package,
NetworkConnectivityReceiver::class.java.name)
val state = if (enabled) {
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
} else {
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
}
packageManager.setComponentEnabledSetting(
componentName,
state,
PackageManager.DONT_KILL_APP
)
}
ACTION_MY_PACKAGE_REPLACED で全ての処理をやるのではなく ChangedPackages でも判断できる
val packages : ChangedPackages = packageManager.getChangedPackages(prefs.getPackageSequenceNumber())
prefs.setPackageSequenceNumber(packages.getSequenceNumber())
Background Limits
Foreground Service にすべきものならそうするForeground のもの
- Visible App
- Foreground Service
- Foreground Client に bound されている Service
- ForeGround Client への Content Provider
- AccessibilityService, NotificationListenerService, AbstractAccountAuthenticator, WallpaperServiceなどの例外もある
- Not visible
- Non-Foreground Service
- JobService
- BroadcastReceiver
Grace Period : Service が background に置かれてから1分程度は生きている
Whitelist
- Notification action
- High Priority FCM message
- SMS/MMS delivery
- background task には WorkManager を使う
- IntentService は JobIntentService に置き換える
JobScheduler の振る舞いについてよく理解するまで Android L で JobScheduler を使わないほうがいい
L と M の first release では JobScheduler は同じ constrains の2つの job を正しく実行しない問題がある
この問題の workaround として、同じ constrains の2つの job をスケジューリングすればよい(がどうするかは言ってない)
MR1 以降は JobScheduler はちゃんと動いているが、minimum latecy を 0 にセットするのはやめたほうがよい
失敗したときの処理は backoff でやること
backoff を超えて reschedule しないこと
PendingIntent の向き先を Service から explicit な BroadcastReceiver に変更し、30秒以内に goAsync() を呼び出す
または BroadcastReceiver 内で WorkManager を使う
外部からの time-sensitive な trigger が必要なら Firebase Cloud Messaging を使う
high priority messages はデバイスが DOZE でも起きるので使いすぎはよくない
10秒以内に実行し終えるならそのままそこで処理をし、それ以上かかるなら WorkManager を使う
ユーザーが明示的に始めた時間のかかる処理は Foreground Service で実行する
Maps Navigation, fitness tracking, playing music など
Photo Broadcasts
N 以降では Photo Broadcasts が起こらないので、代わりに ContentUris をトリガーとした work を使う
val constrains = Constraints.Builder()
.constraints.addContentUriTrigger(SOME_URI, true)
...
.build()
val work = OneTimeWorkRequest.Builder(MyWork::class.java)
.setConstraints(constrains)
.build()
Background Location
O 以降のデバイスでは Background Location の制限は targetSdkVersion によらず適用される対応として
- 1. Geofencing を使う : 100個までしか Geofencing を active にできないので、必要に応じて動的に変えるなどの対策をとる
- 2. Beacon による Nearby Notification を使う
- 3. FusedLocationProvider の Batch 処理
fun createLocationRequest() { ... val request = LocationRequest() request.interval = 10L * 60L * 1000L request.maxWaitTime = 30L * 60L * 1000L }
- 4. Passive Location を使う
fun createLocationRequest() { ... val request = LocationRequest() request.interval = 10L * 60L * 1000L request.maxWaitTime = 30L * 60L * 1000L request.fastestInterval = 2L * 60L * 1000L }
Battery
バッテリーに関する機能- Doze(M+)
- Doze on the go (N+)
- App Standby (M~O)
- App Standby Buckets (P+)
- 使用履歴に基づく制限
- アプリは Standby Bucket のどこかに割り当てられる
- 割り当てられた Bucket によって適用される制限が変わる
Battery Saver (P+)
- Screen Off のときは Location を取らない
- 全てのアプリが App Standby
- Background のアプリは Network 処理をできない
- (OLED Devices では)可能なら Dark Theme が有効になる
Testing
Testing Doze
$ adb shell dumpsys deviceidle force-idle
Testing App Standby
$ adb shell dumpsys battery unplug
$ adb shell am get-inactive <package-name>
$ adb shell am set-inactive <package-name> true
Testing App Standby Buckets
1. $ adb shell dumpsys battery unplug
2. $ adb shell am get-standby-bucket <package name>
10 ACTIVE
20 WORKING_SET
30 FREQUENT
40 RARE
3. $ adb shell am set-standby-bucket <package name> <bucket>
4. API: UsageStatsManager.getAppStandbyBucket()
Testing Battery Saver
$ adb shell dumpsys battery unplug
$ adb shell settings put global low_power 1
<do your tests>
$ adb shell dumpsys battery reset
API: PowerManager.isPowerSaveMode()
PowerManager.ACTION_POWER_SAVE_MODE_CHANGED
アプリに Dark Theme があるなら Save Mode のときは Dark Theme にするという選択肢OLED Devices なら電池の節約になる
Modern features
- Notification Channels
- Display Cutout : Developer Options で Cutout モードにできる
- Picture in Picture
- Multi-display
non-SDK interface
DP1 で non-SDK interface の使用を制限し、使われていたら Toast や log で警告を出すようにしたDP2 ではメソッドが単に動かなくなるので、アプリがクラッシュすることになる
将来的には StrictMode に新しい VM policy を追加する
これを使って全ての non-SDK API を検出できる
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectNonSdkApiUsage().build())
non-SDK の使用がライブラリ内で起こるかもしれないので、これでチェックすることが重要
https://developer.android.com/distribute/best-practices/develop/target-sdk
0 件のコメント:
コメントを投稿