First of all, I looked at these;
I have a streaming application used by almost a million people. I'm using foreground service for the player. I didn't implement MediaSession
yet. I have a 99.95% crash-free sessions. So this app works on all versions but I started getting crash reports(ANR) with android 9. This crash occurs only Samsung
phones, especially s9, s9+, s10, s10+, note9
models.
I tried these,
startForeground()
method in onCreate()
Service.startForeground()
before Context.stopService()
I read some comments from developers of Google, they said it's just Intended Behavior
. I wonder is it occurred by Samsung's system or Android OS. Does anyone have an opinion about this? How can I fix this?
A started service can use the startForeground(int, android. app. Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory.
From Google's docs on Android 8.0 behavior changes: The system allows apps to call Context. startForegroundService() even while the app is in the background. However, the app must call that service's startForeground() method within five seconds after the service is created.
NO MORE STARTSERVICE - The new context. startForegroundService() method starts a foreground service but with a implicit contract that service will call start foreground within 5 second of its creation. You can also call startService and startForegroundService for different OS version.
context. startForegroundService(intent); Inside the service, usually in onStartCommand() , you can request that your service run in the foreground. To do so, call startForeground() .
After too much struggle with this crash finally, I fixed this exception completely and find the solution.
Make sure that have done this stuff in your service I list them as below : (some of this stuff are repetitious as mentioned in another answer I just write them again).
1- Call
startForeground()
in both onCreate and onStartCommand.(it's ok to call startForeground() many times)
@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 your service using
context.stopService()
, there is no need to call stopForeground() or 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 your service using
ContextCompat.startForegroundService()
it will handle different API versions.
ContextCompat.startForegroundService( context, NavigationService.getStartIntent(context) );
4- If your service has actions ( need pending Intents ) handle your pending intents with a Broadcast receiver rather than your current service( it will call your service on Create() and can be dangerous, or use PendingIntent.FLAG_NO_CREATE) , it's a good practice to have a specific Broadcast receiver for handling your service notification actions, I mean create all your pending intents using 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- Always before stop your service make sure that your service is created and started (I Create a global class that has my service state)
if (navigationServiceStatus == STARTED) { serviceManager.stopNavigationService(); }
6- Set your notificationId to a long number such as 121412.
7- Using NotificationCompat.Builder will handle different API versions you just need to create notification channel for Build versions >= Build.VERSION_CODES.O.(This one is not a solution just make your code more readable)
8- Add
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
permission to your manifest. (this one is mentioned in android docs) Android Foreground Service
hope it helps :))
I was waiting my crash report to share the solution. I didn't get any crash or ANR almost 20 days. I want to share my solution. It can help those who encounter this problem.
In onCreate()
method
onCreate()
. Official doc Service.startForeground()
method after Context.startForegroundService()
method. In my prepareAndStartForeground()
method. Note: I don't know why but ContextCompat.startForegroundService() doesn't work properly.
For this reason, I've added manually same function to my service class instead of calling ContextCompat.startForegroundService()
private fun startForegroundService(intent: Intent) { if (Build.VERSION.SDK_INT >= 26) { context.startForegroundService(intent) } else { // Pre-O behavior. context.startService(intent) } }
prepareAndStartForeground()
method
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) } }
It's my onCreate()
override fun onCreate() { super.onCreate() createNotificationChannel() prepareAndStartForeground() }
My 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
methods like these
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 }
We need to clear something when onDestroy() calling
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 }
I hope this solution works properly for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With