I wrote a minimal example to demonstrate the bug. Clicking the button starts the foreground service. The text on the button changes from "▶" to "❚❚". When you press it again, the service stops and the text changes back.
- If you run the program and then exit, everything works as it should.
- If you start the program, start the service, then stop service and exit from the program, then everything is as it should be.
- But if you run the program, start the service and then exit from the program, the process remains hanging in memory. Incl. The red stop button is active in Android Studio. I tried with and without onTaskRemoved, played with the android:stopWithTask manifest flag - it didn’t help. Although the service itself stops (Toast displays: "service stopped").
Here's the code.
MainActivity.kt
package com.example.test_fgsvc
import android.app.Service
import android.content.Intent
import android.os.Bundle
import android.os.IBinder
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.RectangleShape
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.Context
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING
import android.graphics.Color
import android.os.Build
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat.startForegroundService
import com.example.test_fgsvc.ui.theme.TestfgsvcTheme
class MyService : Service() {
private fun createNotification(): Notification {
val notificationChannelId = "ENDLESS SERVICE CHANNEL"
// depending on the Android API that we're dealing with we will have
// to use a specific method to create the notification
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager;
val channel = NotificationChannel(
notificationChannelId,
"Endless Service notifications channel",
NotificationManager.IMPORTANCE_HIGH
).let {
it.description = "Endless Service channel"
it.enableLights(true)
it.lightColor = Color.RED
it.enableVibration(true)
it.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
it
}
notificationManager.createNotificationChannel(channel)
}
val intent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
action = Intent.ACTION_MAIN
addCategory(Intent.CATEGORY_LAUNCHER)
putExtra("type", type)
}
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.getActivity(this, 0, intent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
} else {
PendingIntent.getActivity(this, 0, intent, FLAG_UPDATE_CURRENT)
}
val builder: Notification.Builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Notification.Builder(
this,
notificationChannelId
) else Notification.Builder(this)
return builder
.setContentTitle("saybot service")
.setContentText("saybot")
.setContentIntent(pendingIntent)
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker("Ticker text")
.setPriority(Notification.PRIORITY_HIGH) // for under android 26 compatibility
.build()
}
override fun onDestroy()
{
Toast.makeText(this, "service stopped", Toast.LENGTH_LONG).show()
}
@RequiresApi(Build.VERSION_CODES.Q)
override fun onCreate() {
startForeground(1, createNotification(), FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING)
Toast.makeText(this, "service started", Toast.LENGTH_LONG).show()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
return START_NOT_STICKY
}
override fun onBind(intent: Intent): IBinder {
TODO("Return the communication channel to the service.")
}
override fun onTaskRemoved(rootIntent: Intent?) {
stopSelf()
}
}
class MainActivity : ComponentActivity() {
val btcap = listOf("❚❚", "▶")
lateinit var ctx : Context
val mysvcIntent : Intent = Intent()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ctx = this
mysvcIntent.setClass(ctx, MyService::class.java)
setContent {
TestfgsvcTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
//Greeting("Android")
Box(
modifier = Modifier
.fillMaxSize()
) {
var btnChecked by remember { mutableStateOf(false) }
Button(
enabled = true,
shape = RectangleShape,
onClick = {
if (btnChecked) { // pause
ctx.stopService(mysvcIntent)
} else { // play
startForegroundService(ctx, mysvcIntent)
}
btnChecked = !btnChecked
},
modifier = Modifier
.align(Alignment.Center)
) {
Text(btcap[if (btnChecked) 0 else 1])
}
}
}
}
}
} // onCreate
/*override fun onDestroy() {
super.onDestroy()
ctx.stopService(mysvcIntent)
}*/
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Testfgsvc"
tools:targetApi="31">
<service
android:name=".MyService"
android:enabled="true"
android:exported="false"
android:stopWithTask="false"
android:foregroundServiceType="remoteMessaging" />
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Testfgsvc">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Why doesn't the program terminate, and how can I fix it?