3

maybe this is yet another question about WorkManager, but I really can't find a solution...

I'm trying, as the title suggests, to run a periodic work every 15 minutes. Actually in the worker I'm polling some data every minute. After every poll, every almost 1 second I check if the worker is being stopped and if so return, otherwise keep waiting until 1 minute is reached and poll data again.

According to the documentation this should work and indeed it is, until I kill the app from the recent app screen.

Here is the code:

package com.qsea.app.cordova;

import android.content.Context;

import android.os.SystemClock;

import android.util.Log;

import androidx.work.Worker;
import androidx.work.WorkerParameters;

public class ServerListenerWorker extends Worker {

    public static final String TAG = "ServerListenerWorker";

    public ServerListenerWorker(
        Context appContext,
        WorkerParameters workerParams
    ) {
        super(appContext, workerParams);
    }

    @Override
    public Result doWork() {

        Log.d(TAG, "Doing work");

        final long startTime = SystemClock.elapsedRealtime();
        final int maxDelta = 840000; // 14 minutes

        while (true) {
            // I did this to stop this worker until 15 minutes
            // and then let the next worker run
            if (SystemClock.elapsedRealtime() - startTime >= maxDelta) {
                break;
            }

            // Here I'm polling data, if the polling results in a failure
            // I return Result.retry()

            // It avoid waiting if it remains only 1 minute until the max time
            if (SystemClock.elapsedRealtime() - startTime >= (maxDelta - 60000)) {
                break;
            }
            for (int i = 0; i < 60; i++) {
                SystemClock.sleep(950);
                // Here it checks if it is stopped
                if (isStopped()) {
                    Log.d(TAG, "Detected stop"); // this is actually never reached
                    break;
                }
            }
        }
        return Result.success();
    }
}

And I do as following to start the work

Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(ServerListenerWorker.class, 15, TimeUnit.MINUTES)
    .addTag(serverListenerWorkerUID)
    .setConstraints(constraints)
    .build();
WorkManager.getInstance(cordova.getActivity()).enqueueUniquePeriodicWork(serverListenerWorkerUID, ExistingPeriodicWorkPolicy.KEEP, periodicWorkRequest);

I check the correct working of the worker by viewing the log of my server (If I receive the request it is working fine) and it seems to work ok until I close the app. Then it stops running the actual worker and never run a worker again until I reopen the app, where the work seems to be enqueued and resumes right after the app opening.

Am I doing something wrong in the initialization? I have also <service android:name=".ServerListenerWorker" android:permission="android.permission.BIND_JOB_SERVICE" /> in my AndroidManifest.xml

Is this expected behavior?

I read that chinese ROM have additional restriction to background services and I have a HUAWEI, which seems to be the worst in this. So could it be the device? And if so, how do Facebook, WhatsApp, Instagram and others manage this to work even in these devices?

  • In market and on actual devices it will happen. As actual devices are not stock android phones and are from chinese manufacturers. They have customised for stopping all the other processes and keeps only their own, google, whatsapp, facebook when apps are closed. It is a very long story, i have gone through. – Rushikant Pawar Feb 08 '20 at 10:42
  • What can be done about this? I just tried to ignore optimization for my app, but nothing changed... – Giuliano Collacchioni Feb 08 '20 at 11:00
  • @GC, If your code is perfect ( running okay on set of emulators and on actual standard device like samsung, sony ) then you can try changing CUSTOM settings of phones specially for chinese modified roms like ColorOs, MUIUI, OxygenOS, Fun2Os this list never ends – Rushikant Pawar Feb 08 '20 at 12:33
  • I'm making some tests and I managed to make it work, tweaking battery optimisation settings. I'm trying to find a solution that solves this programmatically (not automatically but asking the user to make those changes). I'll write a solution if I find anything that works – Giuliano Collacchioni Feb 08 '20 at 13:26
  • @GC, If you could write the code for maximum phone manufacturers then please do not forget to answer your own question, it will help the community. – Rushikant Pawar Feb 08 '20 at 13:40
  • Here a reference to another question that could help solve this problem: https://stackoverflow.com/a/48641229/9228492 – Giuliano Collacchioni Feb 08 '20 at 13:42
  • @GC, I forgotten to mention HUAWEI too, But i am not bothering about chinese manufacturers, As a developer Google bothers me, WorkManager library was started to overcome all previous libraries inefficiencies to run over chinese manufacturers. Still chinese manufacturers blocks this one too. So Google must impose restrictions on modifiers and Must limit them for UI modifications only and Must not be allowed to change the way android meant to be. – Rushikant Pawar Feb 08 '20 at 13:46
  • 1
    Ok, I wrote this code: `Activity activity = cordova.getActivity(); activity.startActivityForResult(new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS), ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS_RESULT);`, onActivityResult I call again startActivityResult using the technique referenced before and, after the user flagged the app to ignore the battery optimization, I start the worker. This should be accompanied by an explanation for the user to understand what to do. But, tested in Huawei P smart 2019, it works! I'll clear the code and make an answer to this question soon. – Giuliano Collacchioni Feb 08 '20 at 14:36
  • ~"**I check if the worker is being stopped**". How do you do that? – IgorGanapolsky Apr 30 '20 at 21:48
  • I printed the output from `adb shell dumpsys jobscheduler` (command found (here)[https://stackoverflow.com/questions/55879642/adb-command-to-list-all-scheduled-work-using-workmanager]) and searched for any job scheduled for my app. By the way, facing the reality, I'll probably convert the code I made to use Firebase Cloud Messaging – Giuliano Collacchioni Apr 30 '20 at 22:46

1 Answers1

5

Let's split this in two different problems.

  1. What are you doing in your workers is battery heavy and would be better if you use a foreground service that notifies the user that your application is running continuously in the background. You can also use WorkManager with the newly introduced support for long-running workers (that under the hood uses a foreground service). More on this in the documentation.

  2. If have a problem with a specific OEMs, please open an issue on the Android issuetracker as this maybe a CDD violation. Google can contact the OEM and request that they fix the ROM. This is going to take time, in the meanwhile, you can take a look at sites like don't kill my app to understand what are the constraints on a specific device and use a library like autostarter to help the user to navigate to the right setting.

BTW, you don't need to be list your workers in the AndroidManifest.xml file

pfmaggi
  • 6,116
  • 22
  • 43