担当サービスのOreo対応した話。
targetSdkVersion
を 26にあげると対応が必須になる。
対応は主にここに書いてあることをやることになる。
Background Execution Limits | Android Developers
以下やったこと、ハマったところを列挙。
NotificationChannel の追加
通知が届かなくなるので対応必須。
このあたりを参考にした。
https://github.com/googlesamples/android-testdpc/blob/master/app/src/main/java/com/afwsamples/testdpc/common/NotificationUtil.java
暗黙的broadcastを明示的broadcastへ置き換え
android.intent.action.PACKAGE_REMOVED
を android.intent.action.MY_PACKAGE_REPLACED
に置き換え。
https://developer.android.com/about/versions/oreo/background.html#broadcasts
担当サービスはこれだけだったが、他にも色々あるので要確認。
background で動かしている Service の対応
ここに書いてあるように、background で startService() が呼ばれるものはクラッシュするので対応する。
https://developer.android.com/about/versions/oreo/background.html#migration
ただし、
NotificationManager.startServiceInForeground() instead of startService().
と書いてあるが、実際は ContextCompat.startServiceInForeground()
。
上記メソッドを叩いた後、 Service.startForeground()
5秒以内に呼ばないとクラッシュする(break point で止めても例外なし)など色々曲者。
こちらにNGケースが詳しく書いてある。
Android Oからのバックグラウンド・サービスの制限事項を実演する。 - Qiita
また、 WakefulBroadcastReceiver
は内部で startService()
を使用しているのでクラッシュする。
もともと deprecated だったのでこのタイミングで完全廃止。
BroadcastReceiver
にして、後述の JobDispatcher
と組み合わせる。
対応にあたって、IntentService を使い続けるのはしんどかったので、該当の Service は JobScheduler に置き換えることにした。
しかし担当サービスの minSDK が 16 以上なこともあり、 Firebase の JobDispatcher を導入する。
21以上だったら JobScheduler を使うといいと思う。
JobDispatcher
backgroundから呼ばれる Service
を job に置換していく。
大枠こんな感じ。
before
<service android:name=".MyIntentService"/>
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
public static void startService(final Context context) {
context.startService(new Intent(context, MyIntentService.class));
}
@Override
protected void onHandleIntent(final Intent intent) {
// do something
}
}
after (kotlin 化もついでに..)
<service
android:name=".MyJobService"
android:exported="false">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
</intent-filter>
</service>
class MyJobService : SimpleJobService() {
override fun onRunJob(parameters: JobParameters?): Int {
// do something
return JobService.RESULT_SUCCESS
}
}
呼び出し
val dispatcher: FirebaseJobDispatcher = FirebaseJobDispatcher(GooglePlayDriver(context))
val myJob: Job = dispatcher.newJobBuilder()
.setService(MyJobService::class.java)
.setTag("my-unique-tag")
.setTrigger(Trigger.NOW) // NOW と言いつつも、30秒後ぐらいに発動する
.build()
dispatcher.mustSchedule(myJob)
他にも色々オプション設定があるので公式参照。
まとめ
ここまで終わったら後はひたすら動作確認!