Прежде всего, Я посмотрел на это;
У меня есть потоковое приложение, которым пользуются почти миллион человек. Я использую службу переднего плана для игрока. Я еще не реализовал MediaSession. У меня 99,95% сеансов без сбоев.
Таким образом, это приложение работает на всех версиях, но я начал получать отчеты о сбоях (ANR) с Android 9. Этот сбой происходит только на Samsung телефонах, особенно на s9, s9+, s10, s10+, note9 моделях.
Я попробовал эти,
startForeground() в onCreate()Service.startForeground() до Context.stopService()Я читал некоторые комментарии от разработчиков Google, они сказали, что это просто Intended Behavior. Интересно, это произошло в системе Samsung или в ОС Android? У кого-нибудь есть мнение по этому поводу? Как я могу это исправить?
Это радио/музыкальное приложение, оно просто воспроизводит URL потока. У меня не было сбоев на моем телефоне. Некоторые пользователи говорят, что сбой происходит при прослушивании музыки в фоновом режиме.
Я не удивлюсь, если есть шанс, что это произойдет с идеально написанным приложением, поскольку нет гарантии, что ваш код будет вызван вовремя. В то время как отдельные фрагменты кода в этот ответ представляют собой беспорядок, обходной путь, похоже, обещает: запустить службу через привязку, переместить ее на передний план, тогда использовать startForegroundService() и отменить привязку через какое-то время.
Верно. У вас есть короткое окно после запуска службы переднего плана через startForegroundService для вызова службы startForeground I. По какой-то причине это происходит недостаточно быстро на этих устройствах.
Если это локализовано только для устройств Samsung, это может означать, что у них есть ошибка в их адаптированной версии (версиях) AOSP для Android 9, а не в вашем коде. Любые обходные пути будут чистой догадкой, пока это не будет точно воспроизведено.
Спасибо за ваши ответы @CommonsWare, GabeSechan, Марк Кин. Эти подходы на самом деле не работали для меня. Я ждал большего процента использования, чтобы написать сюда. Я исправил эту проблему, и я больше не получаю этот сбой. Я поделюсь своей реализацией в качестве ответа
Привет, Беязид, если у вас есть какой-нибудь совет для еще одного отчаянного разработчика, мы будем очень рады. Устройства Samsung только на моей стороне, а также. Перепробовал много методов, ни один из них не работает, сумасшедший ... спасибо за возможный совет.
Привет @MenionAsamm, я согласен, это действительно неприятная проблема. Я поделился своей реализацией.
Я также наблюдал это на Huawei P20 Lite (с Android 9).
Привет, ребята, спасибо за ваши вопросы и мнения. Но я сменил компанию и уже год не работаю над этим или подобными проектами. Я поделился решением, которое сработало для меня, но я точно не помню подход. Возможно, это не лучший подход, но он был проработан для проекта. Лучший.
После того, как у меня возникла такая же проблема с теми же телефонами, я внес несколько изменений, и сбои исчезли. Я не уверен, что сделал трюк, но я предполагаю, что вызов startForeground как в onCreate, так и в onStartCommand. Я не уверен, зачем это нужно, если служба уже запущена и в onCreate все вызывалось правильно.
Другие изменения: - Изменение serviceId на какое-то меньшее число (1-10) - Вызов startFororegroundService реже через одноэлементный синхронный класс (ранее это было реализовано при сбоях, чтобы предотвратить остановку службы до ее запуска с обратным вызовом из onStartCommand, но теперь он также фильтрует вызовы, если служба уже запущена). - с помощью START_REDELIVER_INTENT (ни на что не должно влиять)
Проблема возникает на упомянутых телефонах только у некоторых пользователей, поэтому я подозреваю, что она связана с каким-то новым обновлением от Samsung и в конечном итоге будет исправлена.
вам нужно startForeground onStartCommand для случая вызова Context.startForegroundService(), когда Сервис уже создан.
Я ждал отчета о сбое, чтобы поделиться решением. У меня не было сбоев или ANR почти 20 дней. Я хочу поделиться своим решением. Это может помочь тем, кто столкнулся с этой проблемой.
В методе onCreate()
onCreate(). Официальный документService.startForeground() после метода Context.startForegroundService(). По моему prepareAndStartForeground() методу.
Note: I don't know why but ContextCompat.startForegroundService() doesn't work properly.
По этой причине я вручную добавил ту же функцию в свой класс обслуживания вместо вызова 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
}
Я надеюсь, что это решение работает должным образом для вас.
Спасибо, что поделились своей реализацией. Кажется, у вас есть соединение на основе привязки. Возможно, это может быть причиной основного различия, почему в остальном одинаковое решение работает для вас. Спасибо, что поделился!
Привет, любая конкретная причина для "я не знаю почему, но ContextCompat.startForegroundService() не работает должным образом".
Привет, не могли бы вы уточнить, как вы создаете эту услугу? потому что метод службы onCreate вызывается после вызова: ContextCompat.startForegroundService(контекст, намерение), и здесь вы вызываете: startForegroundService для вашей службы onCreate.
@ingsaurabh На самом деле, я не знаю, потому что я не видел никакой разницы в журналах или каких-либо сбоев. Я могу только сказать, что ContextCompat.startForegroundService() не работает для моего кода :)
@CookieMonster startForegroundService — это метод, вызывающий context.startForegroundService. Вы можете увидеть тот же вызов внутри ContextCompat.startForegroundService(контекст, намерение)
@Beyazid, спасибо за ответ, но я не уверен, что понимаю. Является ли метод onCreate в вашем примере onCreate MusicService? потому что если это так, похоже, вы дважды вызываете startForegroundService. один раз сформируйте другое место в приложении (чтобы «запустить» службу onCreate) и второй раз в службе onCreate.
Привет, @Beyazid, проблема решена и больше никогда не отображается в отчете о сбое? Спасибо
@ Beyazid Я тоже в замешательстве. пожалуйста, прокомментируйте ниже, где находится метод «onCreate ()». вызов службы в onCreate Method of Service для меня не имеет смысла
@Beyazid, не могли бы вы ответить на комментарий CookieMonster
ContextCompat.startForegroundService работает исправно
Ответ не ясен. Ваш метод onCreate из какого класса, вы вызываете startForegroundService из onCreate той же службы?
Привет, ребята, спасибо за ваши вопросы и мнения. Но я сменил компанию и уже год не работаю над этим или подобными проектами. Эти строки кода у меня работали, но я точно не помню подход. Возможно, это не лучший подход, но он был проработан для проекта. Лучший.
Тогда это не должен быть принятый лучший ответ, так как автор пишет "но я точно не помню подход. Наверное, это не лучший подход". Логику в этом примере кода проследить очень сложно.
@mobile-visuals Никто не претендует на звание лучшего ответа. Я задал вопрос и тоже ответил на него. Это было решением для меня. Я принял это, поэтому это принятый ответ :)
Компонент Android Service немного сложен для правильной работы, особенно в более поздних версиях Android, где ОС добавляет дополнительные ограничения. Как упоминалось в других ответах, при запуске Service используйте ContextCompat.startForegroundService(). Далее, в Service.onStartCommand(), немедленно звоните startForeground(). Сохраните Notification, которое вы хотите отобразить как поле участника, и используйте его, если оно не равно нулю. Пример:
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
}
Всегда возвращайте START_STICKY в свой Service. Все остальное, вероятно, неправильно, особенно если вы делаете какой-либо аудиоплеер. На самом деле, если вы делаете аудиоплеер, вам не следует реализовывать свою собственную службу, а вместо этого использовать MediaBrowserServiceCompat (из AndroidX).
Я также рекомендую сообщения в блоге, которые я написал по этому поводу: https://hellsoft.se/how-to-service-on-android-part-3-1e24113152cd
@Tim Пожалуйста, прочитайте сообщение, на которое я ссылаюсь. В этой серии я объясню, почему START_STICKY — это то, что вы должны использовать сегодня. Есть конечно исключения, но в 99% случаев, когда вам нужен Service, это правильный способ сделать это.
Я почти устранил проблему с startForeground() в методах MediaSessionCompat.Callback, таких как onPlay(), onPause().
После слишком долгой борьбы с этим сбоем, наконец, я полностью исправил это исключение и нашел решение.
Убедитесь, что вы сделали это в своем сервисе. Я перечисляю их, как показано ниже: (некоторые из этих вещей повторяются, как упоминалось в другом ответе, я просто пишу их снова).
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- Останавливаться ваш сервис с помощью
context.stopService()
, нет необходимости вызывать остановить передний план () или остановить себя ().
try {
context.stopService(
new Intent(
context,
NavigationService.class
)
);
} catch (Exception ex) {
Crashlytics.logException(ex);
LogManager.e("Service manager can't stop service ", ex);
}
3- Начинать ваш сервис с помощью
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- Установите для идентификатор уведомления длинное число, например 121412.
7- Использование NotificationCompat.Builder будет обрабатывать разные версии API, вам просто нужно создать канал уведомлений для версий сборки> = Build.VERSION_CODES.O. (Это не решение, просто сделайте ваш код более читабельным)
8- Добавить
<uses-permission android:name = "android.permission.FOREGROUND_SERVICE" />
разрешение в ваш манифест. (этот упоминается в документах Android) Служба переднего плана Android
Надеюсь, поможет :))
Почему мы должны делать это и в onCreate, и в onStartCommand?
@Sepehr, что такое MAIN_SERVICE_COMMAND_KEY?
оу, рад видеть тебя здесь, Сепер, после долгого времени... спасибо, для меня это работает как шарм, добавив startForegroundService в onStartCommand.
В соответствии с этим сообщением об ошибке это говорит о том, что когда вы вызываете Context.startForegroundService(), вы должны выдать уведомление, используя метод Service.startForeground(). Это то, что я понимаю.
Я потратил так много времени, выясняя, почему он падает, и не понимая, почему. Проблема заключалась в идентификаторе уведомления для startForeground(). Измените его на значение, отличное от 0. Это работало с Android 11.
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//Create notification here
startForeground(5, builder.build()); //Change the ID to something other than 0
return START_NOT_STICKY;
}
Существуют ли какие-либо пути, по которым startForeground нельзя вызывать? Или пути, по которым основной поток застопорился/спит/выполняет слишком много работы и не может обработать запуск службы?