Making a foreground service run until it's stopped

Viewed 2348

My goal is to have an app run in the background even if the screen is switched off. Like a music player. Now everyone claims that you needed a foreground service for that. I am using a class extending Worker for this:

public class WorkerKlasse extends Worker {
    String CHANNEL_ID = "1";
    Context context;
    public WorkerKlasse(
            @NonNull Context context,
            @NonNull WorkerParameters parameters) {
        super(context, parameters);
        this.context = context;
        notificationManager = (NotificationManager)
                context.getSystemService(NOTIFICATION_SERVICE);
    }
    private NotificationManager notificationManager;
    @RequiresApi(Build.VERSION_CODES.O)
    private void createChannel() {
            int importance = NotificationManager.IMPORTANCE_DEFAULT;
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Kanal", importance);
            channel.setDescription("Beschreibung");
            channel.setSound(null,null);
            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this

                NotificationManager notificationManager = (NotificationManager)context.getSystemService(NOTIFICATION_SERVICE);
            notificationManager.createNotificationChannel(channel);
    }
    @NonNull
    private ForegroundInfo createForegroundInfo(@NonNull String progress) {
        // Build a notification using bytesRead and contentLength

        Context context = getApplicationContext();
        // This PendingIntent can be used to cancel the worker
        PendingIntent intent = WorkManager.getInstance(context)
                .createCancelPendingIntent(getId());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createChannel();
        }

        Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID)
                .setContentTitle("Test")
                .setTicker("Test2")
                .setSound(null)
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setOngoing(true)
                // Add the cancel action to the notification which can
                // be used to cancel the worker
                .addAction(android.R.drawable.ic_delete, "A", intent)
                .build();

        return new ForegroundInfo(1,notification);
    }

    @NonNull
    @Override
    public Result doWork() {
        int testint = getInputData().getInt("testint",0);
        ...
        String progress = "Beginn";
        setForegroundAsync(createForegroundInfo(progress));
        //do something, in my case
        mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.testsound);
        mediaPlayer.start();
        try {
            Thread.sleep(testint);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }            
        return Result.success();
    }

}

(before return Result.success(); the foreground service should stop) and I start with this in MainActivity:

WorkRequest myUploadWork =
        new OneTimeWorkRequest.Builder(WorkerKlasse.class)
                .setInputData(
                        new Data.Builder()
                                .putInt("testint",testint)
                                //...
                                .build()
                )
                .build();

WorkManager
        .getInstance(MainActivity.this)
        .enqueue(myUploadWork);

Yet it does not keep running when the screen goes dark or I tap once on the power button to make it go dark. Only after entering the passcode to get into the phone, it continues. The icon on the top to show it's a foreground service is shown, though.

1 Answers

Worker is about finite job you are performing with the possible interaction with the user. But it is not for possible infinite tasks like music playback.

Foreground service is used to perform any "background" job with visible presentation of the process.

"Background" here means that the user is probably not working with your application directly, but interacts with it sometimes or in a parallel way. For example, listening to the music you are streaming.

And since the Android... I don't remember but it is common nowadays, any foreground service should have a notification always visible for the user. It is good practice.

If you are streaming any music or continuous sounds you probably would like to add controls in this notification so the user would be able to manage the playback.

So, first of all, change worker to the foreground service. And now the main question: What if you would like to schedule the start of the foreground service.

You can create a Alarm, and when it would fire - start the foreground service. Whatsapp for example does that all the time to sync chats.

There are a lot of examples how to create alarm and start foreground services:

https://developer.android.com/guide/components/services#StartingAService

https://developer.android.com/training/scheduling/alarms#set

What about existing solution:

Mediaplayer action mediaPlayer.start(); is not blocking thread. And blocking it by making Thread.sleep is bad practice.

Foreground service can be started with the flag Service.START_STICKY and if you will call Service.startForeground as well, it will be running as long as possible. Without any flow waiting for some mystery timeouts.

Related