In our app, we have periodic sync tasks. So we have implemented Job scheduler to fire every sync interval. When the job gets fired, we are starting an intent service and this job gets killed. Intent service starts the 3rd party sync from internet/Server
Questions:
Is this design ok for Oreo and Nougat?
If the intent service takes few mins to complete, will the system allow?
Ref: Medium link
If your job inent service depend on network restriciton you need to set setRequiredNetworkType as there are some new changes done in oreo developement for job scheduler you shold opt for that part as per your flow please take in to account that
Calling setRequiredNetworkType defines network as a strict requirement for your job. If the network requested is not available your job will never run. See setOverrideDeadline(long) to change this behavior. Calling this method will override any requirements previously defined by setRequiredNetwork(NetworkRequest); you typically only want to call one of these methods.
When your job executes in onStartJob(JobParameters), be sure to use the specific network returned by getNetwork(), otherwise you'll use the default network which may not meet this constraint.
for more details go to this source of the above answer https://developer.android.com/reference/android/app/job/JobInfo.Builder.html#setRequiredNetwork(android.net.NetworkRequest)
Related
After upgrading my phone to Android 12 I started to some unexpected behavior with the expedited work job. That is the code I had:
val workRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
WorkManager.getInstance(context).enqueueUniqueWork(UNIQUE_WORK_NAME, ExistingWorkPolicy.REPLACE, workRequest)
And also I had the getForegroundInfo() overrided to return ForegroundInfo instance.
On Android < 12 my work job worked fine: it is long-running and when it got started, the notification was shown in notification trey, and the job was done.
Now, with Android 12 I don't see any notification icon and if I'll turn the screen off, the job is cancelled exactly in one minute. And immediately gets relaunched.
If I explicitly call setForeground() in doWork() method (I don't know what is the difference), it slightly change the behavior: the notification starts to show (not at once for some reason) but it goes away after some time, when the job is still running. The job itself doesn't get cancelled.
What does it all mean? Do I do something wrong? All I want is to launch long-running expedited job with a notification.
Upd: I've noticed that the same thing happens when I just create a foreground service. It gets stopped in exactly one minute if I navigate away from the app (so the app is in background). So, it seems that the problem is in a foreground service itself, since android uses it implicitly when an expedited work is started.
Did you override getForegroundInfo() in your Worker class? According to the docs:
Any ListenableWorker must implement the getForegroundInfo method if
you would like to request that the task run as an expedited job
If I explicitly call setForeground() in doWork() method (I don't know
what is the difference), it slightly change the behavior:
https://developer.android.com/topic/libraries/architecture/workmanager/advanced/long-running
ListenableWorker now supports the setForegroundAsync() API, and CoroutineWorker supports a suspending setForeground() API. These APIs allow developers to specify that this WorkRequest is important (from a user perspective) or long-running.
On Android < 12 my work job worked fine: it is long-running and when
it got started, the notification was shown in notification trey, and
the job was done.
To maintain backwards compatibility for expedited jobs, WorkManager might run a foreground service on platform versions older than Android 12. Foreground services can display a notification to the user.
When targeting Android 12 or higher, foreground services remain available to you through the corresponding setForeground method.
https://developer.android.com/topic/libraries/architecture/workmanager/how-to/define-work#worker
https://developer.android.com/static/images/guide/background/workmanager_main.svg
Workers don't know whether the work they're doing is expedited or not. But workers can display a notification on some versions of Android when a WorkRequest has been expedited.
To enable this, WorkManager provides the getForegroundInfoAsync() method, which you must implement so WorkManager can display a notification to start a ForegroundService for you where necessary.
Long story short - there is a lot of might, can, etc. What you need to do is debug the JobScheduler and figure out what exactly is happening and then try to figure out why.
https://developer.android.com/topic/libraries/architecture/workmanager/how-to/debugging#use-alb-shell0dumpsys-jobscheduler
Check what exactly Constraint is satisfied or not.
What is your Quota and Internet usage(you have an amount in
milliseconds)
What is your PowerBucket(you might ask the user to exempt you from
battery optimization)
History of executed jobs
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.
When foreground service code is executed, a notification UI will always shown in status bar area.
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)
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)
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)
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:
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).
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.
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
I need to allow user to use my app even if network is not available and make server calls when network is available. I am using alarm manager-broadcast receiver- service pattern. I periodically retry to make api calls. I also added the boot receiver. But when user kills the app using overview screen, the alarms are not triggered again. Is there a better way to implement this common scenario?
You may use:-
1. JobScheduler
2. SyncAdapter
These will help you in case of periodically retry to make API calls
My AppWidgetProvider (which is of course a BroadcastReceiver) is registered to receive CONNECTIVITY_CHANGE, so that I can update the widgets as soon as network is restored (though only if required, i.e. if a previous update was missed due to lack of connectivity).
But as set out here, this will no longer be possible in Android N. The suggestion is to use JobScheduler, which allows you to specify that the Job should run only when connected to a network by way of the .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) method.
However, I can't see how this can be used to replicate my desired behaviour. It seems to me that the .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) method will cause the Job not to run at all if there is no network at the time that the Job is scheduled, and it will not cause the Job to wait patiently until there is a network and then run (which is what I need).
So, how do I retain the behaviour I need, with Android N?
I am using Job Schedulers and it does not depend on whether network is available or not while scheduling the job. It provides certain conditions and when those conditions are satisfied job is scheduled. You can set a repetitive job and also specify minimum time gap between the jobs. For example whenever I connect my phone to charging PlayStore start updating apps as the Job has charging constraint.
If you schedule a job and conditions are not met, it will schedule when network is available. Try to disconnect network and open someone profile on Facebook. It will ask you to load profile on network availability. Now, connect to network and you will get the notification once profile is loaded.
try to add google ping for network change that will help you for
network change and all cause i have same issue when i was developing
with socket services. So may be it will also helps you.
According to https://github.com/ReactiveX/RxAndroid/issues/257#issuecomment-164263215 . interval is just for active code, and if app is not wake up, it will not work. So how to use interval for background scheduling tasks?
Please DO NOT use this solution:
To use interval from RxJava you'll have to make sure your app's process stays alive. One way to do it is to put use the Observable in a foreground service. This is a bad idea because the service is NOT actively delivering value to the user. Waiting for time to pass is not delivering value for the user. Again please DO NOT use this.
AlarmManager and JobScheduler (or it's backport GcmNetworkManager) are far better choices for repeating background activities. If you use AlarmManager.setInexactRepeating() the system can batch jobs from multiple apps together to save battery. Using JobScheduler enables you to execute your background jobs in specific conditions, eg. when the device is connected to the internet or when battery is more than 20%. (Internet is required to check the weather).
interval from RxJava does have it's usage on Android. It's an excellent replacement for Runnable.postDelayed for relatively short durations. It makes the code shorter and more readable.
If you need to schedule a task that should be run even if app is not active anymore then use AlarmManager.
If you need to schedule a task that should be run only when app is active then you can use Observable.interval() and react on emission to execute some code and please don't forget to unsubscribe from the Observable when appropriate (when Activity is paused, etc) so app won't burn the battery!