まず、私はこれらを見ました。
私はほぼ100万人が使用するストリーミングアプリケーションを持っています。プレーヤーにフォアグラウンドサービスを使用しています。 MediaSession
はまだ実装していません。私は99.95%のクラッシュのないセッションを行っています。したがって、このアプリはすべてのバージョンで機能しますが、Android 9)でクラッシュレポート(ANR)を取得し始めました。このクラッシュはSamsung
スマートフォン、特に_s9, s9+, s10, s10+, note9
_モデルでのみ発生します。
私はこれらを試しました、
startForeground()
でonCreate()
メソッドを呼び出すService.startForeground()
の前にContext.stopService()
を呼び出す私はGoogleの開発者からいくつかのコメントを読みました、彼らはそれがちょうど_Intended Behavior
_であると言いました。サムスンのシステムまたはAndroid OS。
クラッシュレポートが解決策を共有するのを待っていました。クラッシュやANRがほぼ20日間発生しませんでした。私のソリューションを共有したいと思います。この問題に遭遇した人を助けることができます。
onCreate()
メソッド内
onCreate()
の上部に通知チャネルを作成しています。 公式ドキュメントService.startForeground()
メソッドの後にContext.startForegroundService()
メソッドを呼び出しています。私のprepareAndStartForeground()
メソッド。注:理由はわかりませんが、ContextCompat.startForegroundService()が正しく機能しません。
このため、ContextCompat.startForegroundService()
を呼び出す代わりに、手動で同じ関数をサービスクラスに追加しました
_private fun startForegroundService(intent: Intent) {
if (Build.VERSION.SDK_INT >= 26) {
context.startForegroundService(intent)
} else {
// Pre-O behavior.
context.startService(intent)
}
}
_
prepareAndStartForeground()
メソッド
_private fun prepareAndStartForeground() {
try {
val intent = Intent(ctx, MusicService::class.Java)
startForegroundService(intent)
val n = mNotificationBuilder.build()
// do sth
startForeground(Define.NOTIFICATION_ID, n)
} catch (e: Exception) {
Log.e(TAG, "startForegroundNotification: " + e.message)
}
}
_
それは私のonCreate()
です
_override fun onCreate() {
super.onCreate()
createNotificationChannel()
prepareAndStartForeground()
}
_
私のonStartCommand()
_override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) {
return START_STICKY_COMPATIBILITY
} else {
//....
//...
}
return START_STICKY
}
_
onRebind
、onBind
、onUnbind
これらのようなメソッド
_internal var binder: IBinder? = null
override fun onRebind(intent: Intent) {
stopForeground(true) // <- remove notification
}
override fun onBind(intent: Intent): IBinder? {
stopForeground(true) // <- remove notification
return binder
}
override fun onUnbind(intent: Intent): Boolean {
prepareAndStartForeground() // <- show notification again
return true
}
_
OnDestroy()を呼び出すときに何かをクリアする必要があります
_ override fun onDestroy() {
super.onDestroy()
releaseService()
}
_
_private fun releaseService() {
stopMedia()
stopTimer()
// sth like these
player = null
mContext = null
afChangeListener = null
mAudioBecomingNoisy = null
handler = null
mNotificationBuilder = null
mNotificationManager = null
mInstance = null
}
_
このソリューションが適切に機能することを願っています。
ついにこのクラッシュに苦しみすぎた後、この例外を完全に修正し、解決策を見つけました。
あなたのサービスでこれを行ったことを確認してください:以下にリストします(このものの一部は、別の回答で述べたように繰り返しが多いので、もう一度書きます)。
1-電話
startForeground()
onCreateとonStartCommandの両方で(startForeground()を呼び出してもかまいません)何度も)
@Override
public void onCreate() {
super.onCreate();
startCommand();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
return START_NOT_STICKY;
}
final int command = intent.getIntExtra(MAIN_SERVICE_COMMAND_KEY, -1);
if (command == MAIN_SERVICE_START_COMMAND) {
startCommand();
return START_STICKY;
}
return START_NOT_STICKY;
}
private void startCommand() {
createNotificationAndStartForeground();
runningStatus.set(STARTED);
}
2-Stopサービスを使用して
context.stopService()
、stopForeground()またはstopSelf()を呼び出す必要はありません。
try {
context.stopService(
new Intent(
context,
NavigationService.class
)
);
} catch (Exception ex) {
Crashlytics.logException(ex);
LogManager.e("Service manager can't stop service ", ex);
}
3-Startサービスを使用して
ContextCompat.startForegroundService()
異なるAPIバージョンを処理します。
ContextCompat.startForegroundService(
context,
NavigationService.getStartIntent(context)
);
4-サービスにアクションがある場合(保留中のインテントが必要)は、保留中のインテントをブロードキャストレシーバー現在のサービスではなく(Create()でサービスを呼び出し、危険な場合があります。またはPendingIntent.FLAG_NO_CREATEを使用します)、サービス通知アクションを処理するための特定のブロードキャストレシーバーを用意することをお勧めします。 、つまり、PendingIntent.getBroadcast()を使用して、保留中のすべてのインテントを作成します。
private PendingIntent getStopActionPendingIntent() {
final Intent stopNotificationIntent = getBroadCastIntent();
stopNotificationIntent.setAction(BROADCAST_STOP_SERVICE);
return getPendingIntent(stopNotificationIntent);
}
private PendingIntent getPendingIntent(final Intent intent) {
return PendingIntent.getBroadcast(
this,
0,
intent,
0
);
}
new NotificationCompat.Builder(this, CHANNEL_ID)
.addAction(
new NotificationCompat.Action(
R.drawable.notification,
getString(R.string.switch_off),
getStopActionPendingIntent()
)
)
5-常にサービスを停止する前にサービスが作成および開始されていることを確認します(サービスの状態を持つグローバルクラスを作成します)
if (navigationServiceStatus == STARTED) {
serviceManager.stopNavigationService();
}
6-notificationIdを121412などの長い数値に設定します。
7-NotificationCompat.Builderを使用すると、ビルドバージョン> = Build.VERSION_CODES.O。(これは、コードを読みやすくするだけの解決策ではありません)
8-追加
<uses-permission Android:name="Android.permission.FOREGROUND_SERVICE" />
マニフェストへのpermission。 (これはAndroid docs)で言及されています) Android Foreground Service
それが役に立てば幸い :))
Android Service
コンポーネントは、特に以降のAndroidバージョンでOSが追加の制限を追加するバージョン)で正しく機能するために少しトリッキーです。前述のように他の回答では、Service
を開始するときにContextCompat.startForegroundService()
を使用します。次に、Service.onStartCommand()
でstartForeground()
をすぐに呼び出します。Notification
メンバーフィールドとして表示し、nullでない限りそれを使用したい場合例:
private var notification:Notification? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (notification == null) {
notification = createDefaultNotification()
}
startForeground(NOTIFICATION_ID, notification)
// Do any additional setup and work herre
return START_STICKY
}
常にService
でSTART_STICKY
を返します。それ以外のものはおそらく間違いです。特に、オーディオプレーヤーを使用している場合はなおさらです。実際、オーディオプレーヤーを使用している場合は、独自のサービスを実装するのではなく、代わりに MediaBrowserServiceCompat
(AndroidXから)を使用してください。
私がこれに書いたブログ投稿もお勧めします: https://hellsoft.se/how-to-service-on-Android-part-3-1e24113152cd
OnPlay()、onPause()などのMediaSessionCompat.CallbackメソッドのstartForeground()の問題をほぼ解消しました。
同じ電話で同じ問題が発生した後、私はいくつかの変更を行い、クラッシュはなくなりました。何がうまくいったのかはわかりませんが、onCreateとonStartCommandの両方でstartForegroundを呼び出していると思います。サービスが既に開始されていて、onCreateですべてが正しく呼び出された場合に、なぜそれが必要なのかわかりません。
その他の変更:-serviceIdをいくつかの低い数値(1-10)に変更-シングルトン同期クラスを介してstartFororegroundServiceを呼び出す頻度を減らす(これは、onStartCommandからのコールバックで開始する前にサービスが停止するのを防ぐためにクラッシュで以前実装されていましたが、今ではまたサービスがすでに開始されている場合は、呼び出しをフィルタリングします)。 -START_REDELIVER_INTENTを使用(何も影響しないはず)
この問題は一部のユーザーのみが対象のスマートフォンで発生しているため、Samsungからの新しいアップデートに関連していると考えられ、最終的には修正される予定です
このエラーメッセージによると、Context.startForegroundService()を呼び出すときに、Service.startForeground()メソッドを使用して通知を発行する必要があるということです。これは私が理解していることです。