In some scenarios, you might face an issue where your app’s functionality is lost after several minutes when users place it in background mode or lock their screens. This can lead to poor user experience, such as Bluetooth devices disconnecting from your app or data sync to the server getting interrupted. Users may then complain about the connectivity and stability of your app.

First, to address this issue, you need to understand the Android app Activity lifecycle. When a user switches to another app, like Facebook, instead of using yours, your app’s activity is terminated and the onDestroy() method is triggered. While this behavior is beneficial for the Android system as it helps free up unused memory and manage battery life, it can be problematic for your app because its states and functionalities get lost.

The next step is to refactor your app to separate background functionalities, like Bluetooth connectivity, into a service. A service is an application component capable of performing long-running operations in the background, independent of the user interface. Here’s a code example for a service:

import android.app.Service

class MyService : Service() {

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onCreate() {
      // Place your service logic here
    }

    override fun onDestroy() {
      // Clean up your service logic here
    }

}

To use this service, you also need to define it in your AndroidManifest.xml. Here is an example (replace the name with your service’s package name):

<application>
    ...
    <service android:enabled="true" android:name="com.victorleungtw.myapp.services.MyService"></service>
</application>

Furthermore, to start this service in your activity (for example, in Activity.onCreate()), add the following line:

startService(Intent(this, MyService::class.java))

And where you wish to stop the service, include this line:

stopService(Intent(this, MyService::class.java))

After implementing these changes, your app will be better structured but won’t yet be able to run indefinitely in the background. To achieve that, you also need to add two more methods within your service:

class MyService : Service() {
    // ... existing code

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val channelId =
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    createNotificationChannel("my_service", "My Background Service")
                } else {
                    // If it's an earlier version, the channel ID is not used
                    ""
                }

        val notification: Notification = Notification.Builder(this, channelId)
                .setContentTitle("Title")
                .setContentText("Text")
                .build()
        startForeground(2001, notification)

        return START_STICKY
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun createNotificationChannel(channelId: String, channelName: String): String {
        val channel = NotificationChannel(channelId,
                channelName, NotificationManager.IMPORTANCE_NONE)
        channel.lightColor = Color.BLUE
        channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        service.createNotificationChannel(channel)
        return channelId
    }
}

Here, a notification is created to inform the user that the app continues to run in the background. It might look something like this:

That’s it. Now your users can multitask—like browsing Facebook—while your app continues to run in the background, maintaining Bluetooth connectivity, syncing user data, playing music, or performing other tasks depending on your app’s features.