Cheok Yan Cheng
Cheok Yan Cheng

Reputation: 42806

How does Worker.setForegroundAsync work behind the scene?

Currently, after quit the app, to complete a long running task in foreground (So that it will not be killed by OS), this is what I did by using WorkManager.

  1. Use Worker to call ContextCompat.startForegroundService, for starting an IntentService.
  2. Worker will then return Result.success() immediately.
  3. In onHandleIntent on IntentService, it will execute startForeground, before performing long running task.

Here's the code snippet.

public class SyncWorker extends Worker {
    @NonNull
    @Override
    public Result doWork() {
        final Intent intent = new Intent(WeNoteApplication.instance(), SyncForegroundIntentService.class);

        ContextCompat.startForegroundService(
                WeNoteApplication.instance(),
                intent
        );

        return Result.success();
    }
}

public class SyncForegroundIntentService extends IntentService {
    private static final String TAG = "com.yocto.wenote.sync.SyncIntentService";

    public SyncForegroundIntentService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {

        final Context context = WeNoteApplication.instance();

        NotificationCompat.Builder builder = new NotificationCompat.Builder(...

        startForeground(SYNC_FOREGROUND_INTENT_SERVICE_ID, builder.build());

        // Perform networking operation within foreground service.

        stopForeground(true);

I have strong intention to migrate the code, to make use of setForegroundAsync in WorkManager. The reason is that, under certain rare circumstance, I will get the following crash

Context.startForegroundService() did not then call Service.startForeground()

So, I thought migrating to setForegroundAsync might help eliminating this problem.


However, it isn't clear how setForegroundAsync work behind the scene, by looking at WorkForegroundUpdater souce code.

Here's the code snippet of setForegroundAsync code example from https://developer.android.com/topic/libraries/architecture/workmanager/advanced/long-running

public class DownloadWorker extends Worker {
    public DownloadWorker(
        @NonNull Context context,
        @NonNull WorkerParameters parameters) {
            ...
    }

    @NonNull
    @Override
    public Result doWork() {
        ...

        // What happens behind the scene?!
        setForegroundAsync(createForegroundInfo(progress));

        // The long running task is still executed in Worker thread.
        download(inputUrl, outputFile);

        return Result.success();
    }

However, it isn't clear whether such migration will achieve equivalent outcome. As, here are their differences.

Before migration to setForegroundAsync

  1. The long running task is executed by IntentService's user thread.
  2. Before long running task is completed, Worker will return immediately.

After migration to setForegroundAsync

  1. The long running task is executed by Worker's user thread.
  2. Worker will only return after long running task is finished executing.

Currently, with my current implementation, I will able to avoid Excessive network usage (background) issue. I am not sure whether with setForegroundAsync, will I still able to avoid Excessive network usage (background) issue?

In short, does anyone has idea how Worker.setForegroundAsync work behind the scene?

Upvotes: 4

Views: 5306

Answers (1)

pfmaggi
pfmaggi

Reputation: 6496

Behind the scene setForegroundAsync() starts a foreground service for you when you're on API >= 26, or on a normal service when running on older Android releases. This is documented in the reference:

Under the hood, WorkManager manages and runs a foreground service on your behalf to execute this WorkRequest, showing the notification provided in ForegroundInfo.

I think that the relevant source is in Processor.java.

Talking about vital's metric Excessive Mobile Network Usage in Background, I don't see how this would change in this case given that, also using WorkManager setForegroundAsync() will move your worker to a ForegroundService on Android Oreo+.
For additional information on how this vital's metric is measured, please, take a look at Play Console's guide.

Upvotes: 3

Related Questions