7

By referring to Proper way to tackle and resolve "Excessive network usage (background)"

After few months of debugging, we are now able to run all network related code in Foreground service.

However, we are still getting "Excessive network usage (background)" warning in Android Vital.

enter image description here

When foreground service code is executed, a notification UI will always shown in status bar area.

enter image description here

When we "quit" our app, we launch the foreground service, using WorkManager. The WorkManager will return immediately, after foreground service is launched.

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);

Side note

We don't think we are sending a lot of data. As you can see our latest release, we fall in the lowest range (0 - 5 MB per hour)

enter image description here


Any idea why we're still getting "Excessive network usage (background)"? Apparently, we no longer perform any networking call in background.

We utilize https://developer.android.com/reference/android/app/Service.html#startForeground(int,%20android.app.Notification) and https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)


Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875
  • Not sure I understand what your expectation is here, its not clear by your other question what you had at first, if all you did was add the `startForeground` to your intent service then I am not sure what you expect as you didn't seem to address the core issue of sending a lot of data – tyczj Mar 04 '19 at 15:32
  • It is not to do with "sending a lot of data". It is to do with "sending data in background", which Google highly discourage - https://developer.android.com/topic/performance/vitals/bg-network-usage Hence, we completely avoid from "sending data in background" by using foreground service. Yet, Android Vital still give us warning. – Cheok Yan Cheng Mar 04 '19 at 15:35
  • Android Vitals gathers data with significant delay and it displays results for last 30 days. I think that Excessive network usage will decrease after a few weeks – lukjar Mar 08 '19 at 09:25
  • @lukjar We did a full release on Feb 23, 2019. After 2 weeks, show no sign of decreasing "Excessive network usage". – Cheok Yan Cheng Mar 08 '19 at 09:29
  • may be this answered here - https://stackoverflow.com/a/54514854/8265484 – Mayur Dabhi Mar 08 '19 at 10:58
  • What if rather than `ForegroundService`, you do stuffs using `WorkManager`. Have you tried that usecase? – Jeel Vankhede Mar 11 '19 at 05:25
  • @JeelVankhede Yes, before implementing `ForegroundService`, we are doing all stuffs within `WorkManager` scope, for past few months. We thought that's the reason we are getting Android Vital warning. Hence, we spend some effort to migrate the code to `ForegroundService`. But, we didn't see any improvement in Android Vital warning. – Cheok Yan Cheng Mar 11 '19 at 05:28
  • Just curious... how often do you expect `SyncForegroundIntentService` to run, and how often does it actually run? – Pablo Baxter Mar 11 '19 at 21:05
  • We have carefully ensure, the time difference between 1st `SyncForegroundIntentService` launching, and 2nd `SyncForegroundIntentService` launching, will be equal or more than 1.5 hour. – Cheok Yan Cheng Mar 12 '19 at 02:20
  • You have a warning about 1M session on all version of your app while you show stats telling you have 40k sessions with your version 1.30 (0.08%?). Are you sure the warning isn't there just because you have a lot of users with old version still running and consuming their data? – Xavier Falempin Mar 13 '19 at 14:52
  • No. According to analytics data, I'm pretty sure almost all my users have upgraded to latest version. – Cheok Yan Cheng Mar 13 '19 at 14:58

2 Answers2

6

You are using a Worker to invoke the ForegroundService. From the Worker's documentation:

Worker classes are instantiated at runtime by WorkManager and the doWork() method is called on a pre-specified background thread (see Configuration.getExecutor()). This method is for synchronous processing of your work, meaning that once you return from that method, the Worker is considered to be finished and will be destroyed. (...) In case the work is preempted for any reason, the same instance of Worker is not reused. This means that doWork() is called exactly once per Worker instance. A new Worker is created if a unit of work needs to be rerun.

A ForegroundService is a Service that you put in the foreground state, that means, the system will not kill the process if it needs CPU or if your app is closed. This and only that. I wasn't able to find the documentation of Android Vital proving this, so this is only my suspicion, but I'm quite positive this is the case: this means that regardless of the fact that you use ForegroundService or not, Android Vital still counts this as background work.

A proper way to move your app’s mobile-network usage to the foreground is to call DownloadManager with proper visibility setting set (as stated in the link you have provided). Please let me know if that helps - if not we'll try something different. Btw, were you able to narrow down the statistics to a specific API version? (there were some background threading changes in 9.0 and 8.0 so this can also be a clue)

Simon
  • 2,643
  • 3
  • 40
  • 61
  • I thought once `WorkManager` returns `Result.success();`, that user thread dead? Another user thread (IntentService) will be spawned, which is responsible to perform foreground job? Using `IntentService` to `startForeground` seem pretty legit - https://stackoverflow.com/a/40927723/72437 ? – Cheok Yan Cheng Mar 13 '19 at 14:54
  • `DownloadManager` would not help. We need to use Google Drive REST API to downloaded file, then perform some complicated logic on the downloaded file. – Cheok Yan Cheng Mar 13 '19 at 14:55
  • @Cheok My point is, since you are starting the ForegroundService inside the Worker, it still counts as background work - that is why Android Vital catches this. The real problem is in my opinion that there are devices on which the synchronization repeats itself to the point of exceeding 50MB/hour. I would try to narrow down the cases: which versions? which devices? which OS versions? I remember that android Lollipop had an excessive downloading bug once - I would look for similar cases (I'll keep thinking about this very interesting issue :)) Cheers! – Simon Mar 14 '19 at 07:58
1

If you're doing this:

When we "quit" our app, we launch the foreground service, using WorkManager. The WorkManager will return immediately, after foreground service is launched.

then you're technically scheduling a worker probably with network constraints every time the user shuts the app.

From the documentation for "Excessive background network usage" link

When an app connects to the mobile network in the background, the app wakes up the CPU and turns on the radio. Doing so repeatedly can run down a device's battery

Thus, even though you're not sending the threshold data of 50MB/ 0.10% of Battery session you're getting this warning cause your app is technically waking up the CPU a lot in the background (for network pings).


Though i'm not sure if this is the problem or not, what you can do is, since even the worker documentation guide says:

WorkManager is intended for tasks that are deferrable—that is, not required to run immediately—and required to run reliably even if the app exits or the device restarts

you can try these:

  1. Scheduling a foreground service as soon as the user shuts the app, instead of scheduling a worker, checking inside the onHandleIntent whether the user is online or not (having a connection and for devices above 7, a flowing internet connection as well).

  2. You can try scheduling a worker to run periodically, lets say every few hours based on your business-side requirements, this could be problem if that's not how you wish to back-up the data, but it serves the real purpose of the worker being a deferred task and not something to be executed immediately.

  3. Not sure about this, never tried, but theoretically looks valid, you can use a Unique Work with a mode called REPLACE to replace the worker and have an initial delay of lets say 30mins, this is a hack, but this will delay running of your worker for 30mins, and in the meanwhile if the user opens and shuts the app again, it will replace the old worker with a new one. This solution has its own drawbacks as well, like sometimes the task wont be scheduled if the user is using the app constantly. But will reduce the total number of times the worker had ran.


Finally, the architecture you're using is valid, the whole thing about using a FG service and a worker to schedule it, just that you're doing it quite often. Source
MadScientist
  • 2,134
  • 14
  • 27