新規アプリは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()
- <receiver
- android:name=".NetworkConnectionReceiver"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
- </intent-filter>
- </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
- $ adb shell dumpsys battery unplug
- $ adb shell am get-inactive <package-name>
- $ adb shell am set-inactive <package-name> true
- 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()
- $ 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
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())
https://developer.android.com/distribute/best-practices/develop/target-sdk
0 件のコメント:
コメントを投稿