Service
のonResume()
でActivity
を開始しようとする音楽プレーヤーがあります。わかりやすくするために数行を削除しましたが、コードは効果的です。
@Override
protected void onResume() {
super.onResume();
startService(new Intent(this, MusicService.class));
}
クラッシュログによると、これはAndroid Pを実行している一部のデバイスで例外をスローしています。
Caused by Java.lang.IllegalStateException: Not allowed to start service Intent { cmp=another.music.player/com.simplecity.amp_library.playback.MusicService }: app is in background uid UidRecord{6a4a9c6 u0a143 TPSL bg:+3m25s199ms idle change:cached procs:1 seq(1283,1283,1283)}
at Android.app.ContextImpl.startServiceCommon(ContextImpl.Java:1577)
at Android.app.ContextImpl.startService(ContextImpl.Java:1532)
at Android.content.ContextWrapper.startService(ContextWrapper.Java:664)
at Android.content.ContextWrapper.startService(ContextWrapper.Java:664)
at com.simplecity.amp_library.utils.MusicServiceConnectionUtils.bindToService(SourceFile:36)
at com.simplecity.amp_library.ui.activities.BaseActivity.bindService(SourceFile:129)
at com.simplecity.amp_library.ui.activities.BaseActivity.onResume(SourceFile:96)
onResume()
(およびsuper.onResume()
)が呼び出された直後に、私のアプリがバックグラウンドになっている可能性はありますか。
これは私には意味がありません。これはプラットフォームのバグでしょうか。このクラッシュの影響を受ける3500+ユーザーはすべてAndroid Pです。
更新:これはProdで私たちのために働いていますが、それは100%ではありません。私は過去1ヶ月に1件のクラッシュレポートを受け取りましたが、そうでなければ100件以上もあったでしょう。これが適切に修正されるまで、これは今のところ私たちの最良の選択肢のようです。たぶん私が300を超えて時間を上げたならば、1つのクラッシュは決して起こらなかったでしょうか?
現在これをテストしていますが、これまでのところうまくいっているようです。さらに結果が表示されたら更新します
class ResumingServiceManager(val lifecycle: Lifecycle) : LifecycleObserver {
init {
lifecycle.addObserver(this)
}
val disposable: CompositeDisposable = CompositeDisposable()
fun startService(context: Context, intent: Intent) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
context.startService(intent)
} else {
Single.just(true)
.delaySubscription(300, TimeUnit.MILLISECONDS)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onSuccess = {
context.startService(intent)
}
).addTo(disposable)
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopped() {
disposable.clear()
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun destroy() {
lifecycle.removeObserver(this)
}
}
onCreate()
でそれを初期化してからonResumeでサービスを開始したいときはいつでもresumingServiceManager.startService(this, intent)
を呼び出すだけです。
ライフサイクルに対応しているため、onSuccessのキャンセルを一時停止してすぐに開閉できるようになったときに使い捨てになる可能性があります。
Googleによる回避策があります。
この問題は将来のAndroidリリースで解決されています。
アプリケーションのクラッシュを回避するための回避策があります。アプリケーションは、ActivityManager.getRunningAppProcesses()を呼び出すことでActivity.onResume()でプロセス状態を取得し、重要度レベルがActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUNDより低い場合はサービスの開始を回避できます。デバイスが完全に起動していない場合、アクティビティはすぐに一時停止され、最終的に完全に起動した後に再開されます。
だから私はそれがそのようにすべきだと思います:
// hack for https://issuetracker.google.com/issues/113122354
List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
if (runningAppProcesses != null) {
int importance = runningAppProcesses.get(0).importance;
// higher importance has lower number (?)
if (importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
URLPlayerService.startActionBroadcastServiceData(PlayerActivity.this);
}
私は回避策としてhandlerを使用しました、そしてそれは100%ではなく、かなり上手く動きます:
// hack for https://issuetracker.google.com/issues/113122354
handler.postDelayed(() -> URLPlayerService.startService(PlayerActivity.this),200);
私たちのチームは同じ問題に直面しました。私のカレンダーは2019年4月5日を示していますが、問題はまだ私のサムスンギャラクシーS9 +、アンドロイド9.0(1 UI)に再現
OnResumeでサービスを開始し、onPauseをアンバインドします。
再現方法
このロジックを使ったアクティビティが画面に表示されたら、端末をロックします。10〜15分は触れないでください。画面ロックを解除した後、アプリがクラッシュします。
修正方法
実際にうまくいく解決策を見つけました。 Android 8以降、Android.os.Handler.postでサービスを開始します(...)
例(Kotlin):
override fun onResume() {
super.onResume()
Handler().post {
val serviceIntent = Intent(activity, SomeService::class.Java)
activity?.startService(serviceIntent)
activity?.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE)
}
}
がんばろう!
彼らはクラッシュを修正し(将来のリリースでは、どちらが正確かわからない)、当面の間回避策を提供した。 https://issuetracker.google.com/issues/110237673#comment9
これは、Android Issue Trackerでは「修正済み」とマークされています。
おそらく、この修正はAndroid Qリリースのうちの1つでリリースされるでしょう。
この問題を解決したグーグラーによると、
アプリケーションのクラッシュを回避するための回避策があります。アプリケーションは
Activity.onResume()
を呼び出してActivityManager.getRunningAppProcesses()
のプロセス状態を取得し、重要度がActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
より低い場合はサービスの開始を回避できます。デバイスが完全に起動していない場合、アクティビティはすぐに一時停止され、最終的に完全に起動した後に再開されます。