GCM Network Manager losing jobs - android

I am trying to use GCM Network Manager to send logs to out backend service. We have an alarm running about every hour that creates a OneoffTask that, when executed, will call the backend service with the log message.
This works, BUT a very large amount of the Tasks are lost (way more than half). At first, I thought it has something to do with our backend, or the network, but after adding a ton of file logging, it turns out that onRunTask in the service is never triggered for these tasks (but they are definitely getting scheduled. Why are these lost? Am I misunderstanding the API, or is OneoffTasks simply not reliable?
This is how the OneoffTask is scheduled:
GcmNetworkManager.getInstance(context).schedule(new OneoffTask.Builder()
.setService(AvroLogService.class)
.setExtras(bundle)
// A mandatory tag which identifies the task
// We add a unique hash to the tag to make sure that
// tasks are logged and not thrown away as dupes.
// See: http://stackoverflow.com/q/34528960/304262
.setTag(java.util.UUID.randomUUID().toString())
// Persist to disk, even across boots:
.setPersisted(true)
// Sets a time frame for the execution of this task in seconds.
// This specifically means that the task can either be
// executed right now, or at latest at a certain point:
.setExecutionWindow(0, TWO_WEEKS_IN_SECONDS)
.build());
Again, not this works, but only part of the messages. For the messages that are subsequently lost, the above code is
definitely executed (I've added file logging to verify this), but there is never a corresponding onRunTask triggered for the lost ones.
I have verified that:
Manifest is updated according to the Network Manager Implementation Guide (https://developers.google.com/cloud-messaging/network-manager)
AvroLogService (my service) extends GcmTaskService
It overrides onRunTask
The app has RECEIVE_BOOT_COMPLETED permission.
AvroLogService does NOT override onStartCommand.
I'm lost. Can someone share insights on this?

As answer above execution time range may be to big. Also I think that You want execute event periodically try use PeriodicTask.Builder instead ofOneoffTask.Builder

As I guess your constant TWO_WEEKS_IN_SECONDS really means 2 WEEKS. In this case you task is eligible for execution in any point of time from now to 2 WEEKS. So, this task doesn't have to be executed every hour.
Try to set execution window in range of one hour or even less (.setExecutionWindow(0, HALF_AN_HOUR_IN_SECONDS))
See google api docs

Related

Can tasks on PeriodicWorkRequest be prioritised?

We utilise a periodic Worker to fetch the location every 15 minutes.
As long as the phone is actively used and internet connection is available, everything goes by the books.
But when the app is in the background for too long and or even worse when it's in the flight-mode, the gaps between the fetches become as long as couple of hours.
I believe this is because the work is batched and it's up to the Android phone to decide when to run it again.
According to the documentation setExpedited() can in fact run a job under a higher urgency and prevent it from being batched.
Marks the WorkRequest as important to the user. In this case,
WorkManager provides an additional signal to the OS that this work is
important.
val workRequest = PeriodicWorkRequestBuilder<WappWorker>(15, TimeUnit.MINUTES).addTag(
TAG
).setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST).build()
I have implemented it this way and successfully compiled and ran it.
But it crashes at runtime:
java.lang.IllegalArgumentException: PeriodicWorkRequests cannot be
expedited
How odd. So this feature seems to be only allowed for OneTimeWorker and not for the PeriodicWorker.
What are my options? Is there any other way to mark my periodic worker jobs with a higher urgency? Thanks

When the android app is force stopped and reopened, will WorkManager executes overdue work or startover?

Assume there is a scheduled work like this:
PeriodicWorkRequestBuilder<RefreshAuthWorker>(
repeatInterval = 15L,
repeatIntervalTimeUnit = TimeUnit.MINUTES,
flexTimeInterval = 5L,
flexTimeIntervalUnit = TimeUnit.MINUTES
)
Let's say, At 10th minute, I forcely killed/stopped the app.
Then reopen the app again after 10 mins.
by this time, the existing/killed work is overdue by 5 mins.
Now, What will the WorkManager do?
1.WorkManager respects the missed overdue work and do it immediately and then schedule the next work.
(or)
2.WorkManager ignores the past overdue work and schedules the next work?
This is a very important scenario to me because, let's say I have to refresh a token every 15 mins. But if WorkManager does like said in 1st point then, by the time I reopen the app, the token is already expired 5 mins ago and the next work is going to happen in 15 mins. So, it's a total of 20 mins with an expired token.
Can somebody who knows any idea what will the WorkManager do in such scenario, please help.
You can't do such things with a WorkManager.
Everything might happen if an application is forced stop. It is a problem of the user if he decides to do so. You should not care.
Are you sure you do not mean - clear from recent?
The priority of WM is saving resources like battery and network data. Timing is not a big concern. The idea is that you need some work to be executed for sure at some point. Like you want to upload a picture to a server.
What WorkManager does is - it creates a job in the JobScheduler. The job is executed when all the constraints are satisfied:
You have implicit constraints related to battery saving. You have some amount of resources that you can use based on your Power Bucket level. Also, the device's state is important. You can't predict when these constraints will be satisfied.
Also you have explicit constraints that you set. Like Connectivity, Battery level and in your case: "a period". But is a no period at all. When you have a "period" work on a higher level in the WorkManager - actually it means - many single jobs in the Job Scheduler. And each one of these jobs has the above implicit constraints and your explicit which is called - timing delay. So you see:
You start the work
WM schedules a job in JS with respective constraints
After 15min timing delay is satisfied
No one can tell what is the status of the implicit constraints. The device might be dozing, or you might have used all of your data usage for the last 24 hours or something like this.
5 At some point when all the constraints are satisfied - the Work starts and when it is finished:
6. You have a new job with the same constraints as before. So in theory your "15min period work" might be executed in 24 hours and after that, it might execute the second time in 15 minutes.

Workmanager does not execute unique jobs with APPEND policy

I've found this weird behaviour of work manger (alpha-12) that it does not execute a job after it was enqueued. The code to enqueue the work is below.
fun enqueue(phoneNumber: String?,priorityId: String? = null): ListenableFuture<WorkInfo> {
return WorkManager.getInstance().run {
val work = OneTimeWorkRequestBuilder<ContentDownloaderWork>()
.addTag(TIMESTAMP)
.setInputData(workDataOf(PHONE to phoneNumber, PRIORITY_ID to priorityId))
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.SECONDS)
.setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
.build()
beginUniqueWork(TIMESTAMP + priorityId.orEmpty(), ExistingWorkPolicy.APPEND, work).enqueue()
getWorkInfoById(work.id)
}
}
I enqueue this work each time on app startup. And what I am doing is killing my app in the middle of the work process that is downloading files from server. It successfully restart every time I reopen my app but for limited number of times.
It seems like it has some sort of queue which gets filled up and does not allow any more jobs to be enqueued. When I check all available jobs by tag it show me that some are canceled and some succeeded but when I try to enqueue a new work it does nothing.
So is it a bug in workmanager or am I doing something wrong?
One important information that is missing in your description is the Android version that you're using in these tests.
WorkManager schedule workers using the JobScheduler API on Android Marshmallow (6.0, API Level 23) and newer, and it schedules a maximum amount of workers using this API at any time (default is 20, maximum is 50). The threshold you're seeing may be linked to this value.
Another important point, is that WorkManager keeps tracks of your workers for you so, if the application is killed while the worker is running, WorkManager automatically restart the worker. You don't need to do anything.
What is not clear to me, is what are you trying to achieve appending a new job every time at the application startup.
Last point, the backoff criteria of 1 second seems a bit aggressive to me. The current default value, looking into the source code, is 30 seconds and the maximum is 5 hours.
However, if you think that there're something wrong (or documented incorrectly) I suggest you to open a bug on WorkManager's public issuetracker.,

How to schedule synchronization for Internet connectivity only

I'm using a Sync Adapter to perform synchronization for my app. It has set automatic sync:
// Inform the system that this account supports sync
ContentResolver.setIsSyncable(account, AuthenticatorService.AUTHORITY, 1);
// Inform the system that this account is eligible for auto sync when the network is up
ContentResolver.setSyncAutomatically(account, AuthenticatorService.AUTHORITY, true);
If I request the sync:
Bundle b = new Bundle();
// Disable sync backoff and ignore sync preferences. In other words...perform sync NOW!
b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
ContentResolver.requestSync(AuthenticatorService.GetAccount(), AuthenticatorService.AUTHORITY, b);
it works well -- as soon as the network is up, SyncAdapter.onPerformSync() is called. However, there's one problem: if the network is up but not connected to Internet (hotspot without tethering for instance), onPerformSync() is still called.
Of course, since it can't reach the server, sync code fails. What is worse, the system thinks everything went OK and wont do any more sync calls unless my data change.
My question is: can I either mark the synchronization as not successful so the system will try it later (for instance on another CONNECTED event or in a few seconds) or better to tell the system to perform the sync only when real Internet connectivity is present?
I could monitor the network state and do a connectivity check by myself but that would defy the whole purpose of sync adapters, wouldn't it. Only workaround I can think of is to schedule periodic synchronization (using ContentResolver.addPeriodicSync()) and when my synchronization code succeeds, turn it off. But I'm not very happy about this either (draining battery).
EDIT: As for the first part of my question, this answer is what I was looking for. It partially solves my problem but if there's consecutive number of "non-working" connections, when the device is associated with the real connection, it would take tens of minutes to sync (because of the exponential backoff algorithm).
Thanks to csenga's suggestion I found a solution: use GcmNetworkManager to schedule the synchronization. It looks a bit stupid as I have to declare yet another service just to call ContentResolver.requestSync() and if something goes wrong I have to reschedule my task instead of setting the SyncResult.stat flags (I'm virtually duplicating Sync Adapter's job here) but still better than to handle my own NETWORK_STATE_CHANGED_ACTION receiver.

Running One off GCM Network Manager task on Push

In an Android app, I perform a data poll from server on push. When the device receives a push it pings the server to get the latest data.
As the user base grows, Server could potentially get 1000's of request at the time of push taking the backend down. I am looking for a good alternative so that I can spread out the server call in a given time window say next 2 hours. What is the a good way to do it?
I was looking into GCM Network Manager One-off task. One thing I am not certain is that even if I set a time-window start now with offset of 2 hours, since the device would be connected to the network when the push is received, it would trigger the server call right away defeating the purpose.
Any suggestions on what might be a good way to resolve this?
I don't think the task will execute immediately, as the GCM network manager doesn't execute only when the network is up, but tries to batch jobs together to reduce the number of wakeups and power consumption.
However, to be safe, when you create the OneoffTask, you can set an execution window. There you can set a minimum amount of time before the task will run. I suggest using a random number of seconds, e.g. between 0 and 60 to reduce the potential load on your server.

Categories

Resources