Android JobScheduler onStartJob called multiple times - android

The JobScheduler calls onStartJob() multiple times, although the job finished. Everything works fine, if I schedule one single job and wait until it has finished. However, if I schedule two or more jobs with different IDs at the same time, then onStartJob() is called again after invoking jobFinished().
For example I schedule job 1 and job 2 with exactly the same parameters except the ID, then the order is:
onStartJob() for job 1 and job 2
Both jobs finish, so jobFinished() is invoked for both of them
After that onStartJob() is called again for both jobs with the same ID
My job is very basic and not complicated.
public class MyJobService extends JobService {
#Override
public boolean onStartJob(final JobParameters params) {
new Thread(new Runnable() {
#Override
public void run() {
try {
// do something
} finally {
// do not reschedule
jobFinished(params, false);
}
}
}).start();
// yes, job running in the background
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
// mark my background task as stopped
// do not reschedule
return false;
}
}
I schedule the jobs like this
JobInfo jobInfo = createBaseBuilder(request)
.setMinimumLatency(2_000L)
.setOverrideDeadline(4_000L)
.setRequiresCharging(false)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
int scheduleResult = mJobScheduler.schedule(jobInfo);
// is always success
I don't know what's wrong.

I guess it's caused by the pending Job, so I call mJobScheduler.cancelAll() after the service started, problem resolved.

I think this relates to the Android bug reported here, which has apparently been fixed for Android N but will be present in earlier versions.
The OP is using a setOverrideDeadline(). My understanding of the issue reported in the linked post above is that if the job is running when the override deadline fires, it causes the job to be scheduled to run again.
So the advice is to ensure that the override fires either before the job is scheduled (not sure how that is achieved) or after it has finished. Neither seems particularly satisfactory, but at least it seems to have been fixed in Android N.

this is the problem in android lollypop and Marshmallow. It is fixed in Nougat as explained by Matthew Williams here

Related

Android work manager invokes worker after 10 minutes when the current worker still running

I have periodic worker, which executes for every 15 min. The worker uploads the data files into server. But, when the current worker taking time to upload and still running, after 10 min, the work manager invokes the worker after 10 min which causing an issues as both of them trying to access the db and overriding each other. Is there any way to stop the worker being invoked after 10 min when current worker is still running.
I am using the 2.3.0-alpha03 version.
Can anyone please help?
I would try putting the workers on the same background thread, so the work request gets queued one after the other.
WorkManager.initialize(
context,
new Configuration.Builder()
.setExecutor(Executors.newFixedThreadPool(1))
.build());
You can read more about threading and WorkManager here
If constraints prevent the work request from running you should be able to use an equal constraint on the periodic worker, set the equal state to false before the oneTimeWorker runs and true after the work finishes.
private boolean RUN_WORKER = true;
Constraints constraints = new Constrains.Builder()
.equals(RUN_WORKER)
.build
PeriodicWorkRequest request =
new PeriodicWorkRequest.Builder(myPeriodicWorker.class, 10, TimeUnit.MINUTES)
.setConstraints(constraints)
.build();
WorkManager.getInstance(myContext)
.enqueue(request);
The method that runs OneTimeWorker
RUN_WORKER = false;
OneTimeWorkRequest request = new OneTimeWorkResquest =
new OneTimeWorkRequest.Builder(OneTimeWorker.class)
.build();
WorkManager.getInstance(mContext).getWorkInfoByIdLiveData(request.getId())
.observe(lifecycleOwner, new Observer<WorkInfo>() {
#Override
public void onChanged(#Nullable WorkInfo workInfo) {
if (workInfo != null && workInfo.state == WorkInfo.State.SUCCEEDED) {
displayMessage("Work finished!")
}
RUN_WORKER = true;
}
});
The system stops the work and restart it if the execution time is bigger than 10 minutes
https://developer.android.com/topic/libraries/architecture/workmanager/how-to/managing-work
To avoid the stop and restart, the worker class has a support for ForegroundAsync services, which have worked for me:
https://developer.android.com/topic/libraries/architecture/workmanager/advanced/long-running

WorkManager PeriodicWorkRequest run twice

I have an app that runs a database refresh every 15 mins.
The PeriodicWorkRequest is enqueued in onCreate of my main activity.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
updateDatabasePeriodically();
public void updateDatabasePeriodically() {
PeriodicWorkRequest updateDatabaseRequest =
new PeriodicWorkRequest.Builder(UpdateDatabaseWorker.class, 15, TimeUnit.MINUTES).build();
WorkManager.getInstance(getMainActivityContext()).enqueue(updateDatabaseRequest);
}
UpdateDatabaseWorker:
#Override
public Result doWork() {
// Do the work here--in this case, upload the images.
updateDatabase();
// Indicate whether the task finished successfully with the Result
return Result.success();
}
private void updateDatabase() {
AppDatabase appDatabase = AppDatabase.getInstance(context);
Log.i(TAG, "Updating alerts at: " + DateTimeHelper.getHumanReadableTime(now));
}
The problem is that the work is done twice every time the job executes as can be seen by my log statements
2019-11-22 10:20:23.185 24987-25085/com.package.myapp I/com.package.myapp.UpdateDatabaseWorker: Updating at: 22/11/2019 10:20:23
2019-11-22 10:21:30.510 25309-25408/com.package.myapp I/com.package.myapp.UpdateDatabaseWorker: Updating at: 22/11/2019 10:21:30
2019-11-22 10:34:49.642 25309-26180/com.package.myapp I/com.package.myapp.UpdateDatabaseWorker: Updating at: 22/11/2019 10:34:49
2019-11-22 10:35:23.372 25309-26198/com.package.myapp I/com.package.myapp.UpdateDatabaseWorker: Updating at: 22/11/2019 10:35:23
Why is this happening? Should I be creating the request somewhere else?
Would this be a good solution? https://stackoverflow.com/a/53059601/9137086
I feel like that's a treating the symptom instead of the disease
In this cases please, use an uniqueWorkRequest, otherwise (especially for PeriodicWorkRequest) you will end up with duplicates.
public void updateDatabasePeriodically() {
PeriodicWorkRequest updateDatabaseRequest =
new PeriodicWorkRequest.Builder(UpdateDatabaseWorker.class, 15, TimeUnit.MINUTES).build();
WorkManager.getInstance(getMainActivityContext())
.enqueueUniquePeriodicWork("uniqueWorkName",
ExistingPeriodicWorkPolicy.KEEP,
updateDatabaseRequest);
}
You can take a look at the reference for WorkManager#enqueueUniqueWork for more information on the call and on the ExistingPeriodicWorkPolicy one to see which are the options.
If you don't have requirements to use REPLACE, please use KEEP, it's less expensive and avoid stopping an already working Work.

Firebase Jobdispatcher: Jobs that run endlessly due to an error are never scheduled again

What happens to a recurring job that is not finished due to a crash or Android process cleaning?
In my opinion, it is not possible to reschedule it. It will never be executed again.
Example
public class MyJobService extends JobService {
#Override
public boolean onStartJob(JobParameters job) {
doSomeWork(job);
return true; // true = there is still work going on!
}
private void doSomeWork(final JobParameters job) {
new Thread() {
public void run() {
// do some work, maybe load data from internet
// loading...
// loading...
// app crashes or its process is killed by Android
// this piece of code is never reached!
jobFinished(job, false);
}
}.start();
}
}
As you can see in the code, the job can never be terminated by the crash. It will not be rescheduled afterwards.
If you want to reschedule it with FirebaseJobDispatcher.mustSchedule(), this appears in logcat:
E/FJD. ExternalReceiver: Received execution request for already running job
W/FJD. JobService: Job with tag = ... was already running
even if you run FirebaseJobDispatcher.cancel() before.
Any suggestions?
put your code in try catch block. in catch block call
jobFinished(job, true); . "true" means the job will be rescheduled.
" What happens if Android shuts down the app, e. g. in case of lack of memory?" .In this case os will raise the OutOfMemmory error and your try catch block will handle it.

Job manager in Android

I have a task to run several different jobs in Android app. Each job is long-running and cosumes network, database and file system much. Each job can be run manually by user or scheduled by AlarmManager. It is very important that each job runs till the end, so it needs to continue running after user leaves the app, or even when user does not open the app at all. Jobs have some ID attribute like this:
class Job {
int id;
}
I need this hypothetical JobManager to receive jobs and sort them by ID. If a job with id = 1 is already running, then JobManager should skip all the subsequent jobs with id = 1 until this job is finished. But if a job is submitted with id = 2, then it is accepted and can be run in parallel with the first job.
The jobs should also to keep wake lock until completed, like it is done in CommonsWare's WakefulIntentService.
I have several ideas how to implement this, but all have their drawbacks:
Subclass of the Service class that runs always in background and is automatically restarted, when killed for some reason. Drawbacks: it consumes resources even if not running anything, it is running on UI thread, so we have to manage some threads that can be killed by system as usual, each client has to start the Service and nobody knows, when to stop it.
WakefulIntentService from CommonsWare. Drawbacks: because it is IntentService, it runs only sequentially, so it cannot check for existing running job.
Boolean "running" flag in the database for each job. Check it every time we want to run a job. Drawbacks: too many requests to db, difficult to implement properly, sometimes 2 equal jobs still can run in parallel, not sure about flags staying "true" in case of any unexpected error.
Existing library disigned for this purpose. As for now except CWAC-Wakeful I have found:
Robospice: https://github.com/stephanenicolas/robospice
Android Job Queue: https://github.com/path/android-priority-jobqueue
but still I don't know, how to use these libraries to run exactly one centralized service, that whould accept jobs from any other Activity, Service, BroadcastReceiver, AlarmManager, etc, sort them by ID and run in parallel.
Please advise me what solution can be used in this case.
UPDATE: See below my own solution. I'm not sure, if it works in all possible cases. If you are aware of any problems that may arise with this, please comment.
This seems to be suited for the new JobScheduler API on Lollipop, then you will have to make a wrapper around it to implement all the features that the sdk implementation is missing.
There is a compat library if you need to implement this on versions below Lollipop.
If anybody faces the same problem, here is the solution I came up with. I used Robospice lib, because it is the most robust way of running some jobs on a Service and syncing results back to the Activity. As I did not find any ways to use this lib with WakeLocks, I extended 2 classes: SpiceManager and SpiceRequest. The new classes, WakefulSpiceManager and WakefulSpiceRequest, actually borrow CommonsWare's ideas about WakeLocks, the implementation is very similar.
WakefulSpiceManager:
public class WakefulSpiceManager extends SpiceManager {
private static final String NAME = "WakefulSpiceManager";
private static volatile PowerManager.WakeLock wakeLock;
private Context context;
public WakefulSpiceManager(Context context, Class<? extends SpiceService> spiceServiceClass) {
super(spiceServiceClass);
this.context = context;
start(context);
}
private static synchronized PowerManager.WakeLock getLock(Context context) {
if (wakeLock == null) {
PowerManager mgr = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NAME);
wakeLock.setReferenceCounted(true);
}
return wakeLock;
}
public <T> void execute(WakefulSpiceRequest<T> request, RequestListener<T> requestListener) {
PowerManager.WakeLock lock = getLock(context);
lock.acquire();
request.setLock(lock);
// explicitly avoid caching
super.execute(new CachedSpiceRequest<T>(request, null, ALWAYS_EXPIRED), requestListener);
}
}
WakefulSpiceRequest:
public abstract class WakefulSpiceRequest<R> extends SpiceRequest<R> {
private PowerManager.WakeLock lock;
public WakefulSpiceRequest(Class<R> clazz) {
super(clazz);
}
public void setLock(PowerManager.WakeLock lock) {
this.lock = lock;
}
#Override
public final R loadDataFromNetwork() throws Exception {
try {
return execute();
} finally {
if (lock.isHeld()) {
lock.release();
}
}
}
public abstract R execute() throws Exception;
}
So basically here we acquire the lock every time we are going to send a request from WakefulSpiceManager. After that the lock is passed to the WakefulSpiceRequest. When request finishes its work, it cleans the lock with release() method - this will happen even if the activity with WakefulSpiceManager is already destroyed.
Now we use those classes in usual Robospice's manner, with the only exception that we need to pass only WakefulSpiceRequests to execute on WakefulSpiceManager:
WakefulSpiceManager manager = new WakefulSpiceManager(context, MyService.class);
manager.execute(new WakefulSpiceRequest<MyResult>(MyResult.class) {
#Override
public MyResult execute() throws Exception {
return ...
}
}, new RequestListener<MyResult>() {
#Override
public void onRequestFailure(SpiceException e) {
...
}
#Override
public void onRequestSuccess(MyResult result) {
...
}
});
The new Workmanager will help you schedule tasks in any order you want. You can easily set constraints to the job that you want to be en-queued along with many other advantages over JobScheduler API or alarm manager. Have a look at this video for a brief intro - https://www.youtube.com/watch?v=pErTyQpA390 (WorkManager at 21:44).
EDIT: Updated my ans to show the capabilities of the new API
You will not need ids to handle the jobs with this one. You can simply enqueue the task and the rest will be handled by the API itself.
Some work case scenarios are
WorkManager.getInstance()
.beginWith(workA)
// Note: WorkManager.beginWith() returns a
// WorkContinuation object; the following calls are
// to WorkContinuation methods
.then(workB)
.then(workC)
.enqueue();
WorkManager.getInstance()
// First, run all the A tasks (in parallel):
.beginWith(workA1, workA2, workA3)
// ...when all A tasks are finished, run the single B task:
.then(workB)
// ...then run the C tasks (in any order):
.then(workC1, workC2)
.enqueue();

handler.postDelayed vs. AlarmManager vs

I have a minor problem in one of my apps. It uses a BroadCastReceiver to detect when a call finishes and then performs some minor housekeeping tasks. These have to be delayed for a few seconds, to allow the user to see some data and to ensure that the call log has been updated. I'm currently using handler.postDelayed() for this purpose:
public class CallEndReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
if (DebugFlags.LOG_OUTGOING)
Log.v("CallState changed "
+ intent.getStringExtra(TelephonyManager.EXTRA_STATE));
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE)
.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_IDLE)) {
SharedPreferences prefs = Utils.getPreferences(context);
if (prefs.getBoolean("auto_cancel_notification", true)) {
if (DebugFlags.LOG_OUTGOING)
Log.v("Posting Handler to remove Notification ");
final Handler mHandler = new Handler();
final Runnable mCancelNotification = new Runnable() {
public void run() {
NotificationManager notificationMgr = (NotificationManager) context
.getSystemService(Service.NOTIFICATION_SERVICE);
notificationMgr.cancel(12443);
if (DebugFlags.LOG_OUTGOING)
Log.v("Removing Notification ");
}
};
mHandler.postDelayed(mCancelNotification, 4000);
}
final Handler updateHandler = new Handler();
final Runnable mUpdate = new Runnable() {
public void run() {
if (DebugFlags.LOG_OUTGOING)
Log.v("Starting updateService");
Intent newBackgroundService = new Intent(context,
CallLogUpdateService.class);
context.startService(newBackgroundService);
}
};
updateHandler.postDelayed(mUpdate, 5000);
if (DebugFlags.TRACE_OUTGOING)
Debug.stopMethodTracing();
try
{
// Stopping old Service
Intent backgroundService = new Intent(context,
NetworkCheckService.class);
context.stopService(backgroundService);
context.unregisterReceiver(this);
}
catch(Exception e)
{
Log.e("Fehler beim Entfernen des Receivers", e);
}
}
}
}
Now I have the problem, that this setup works about 90% of the time. In about 10% of cases, the notification isn't removed. I suspect, that the thread dies before the message queue processes the message/runnable.
I'm now thinking about alternatives to postDelayed() and one of my choices is obviously the AlarmManager. However, I'm not sure about the performance impact (or the resources it uses).
Maybe there is a better way to ensure that all messages have been processed before a thread dies or another way to delay the execution of those two bits of code.
Thank you
I'm currently using handler.postDelayed() for this purpose:
That's not a good idea, assuming the BroadcastReceiver is being triggered by a filter in the manifest.
Now I have the problem, that this setup works about 90% of the time. In about 10% of cases, the notification isn't removed. I suspect, that the thread dies before the message queue processes the message/runnable.
More accurately, the process is terminated, taking everything with it.
I'm now thinking about alternatives to postDelayed() and one of my choices is obviously the AlarmManager. However, I'm not sure about the performance impact (or the resources it uses).
It's not that bad. Another possibility is to do your delayed work in an IntentService -- triggered via a call to startService() -- and have it sleep on its background thread for a couple of seconds.
Let's try a new way of doing this. Using RxJava. It's much simpler to prototype and easier to manage lots of threads if you want to ever run hundreds of such delayed tasks concurrently, sequentially, coupled with async tasks, chained with synchronous chained async calls etc.
Firstly, set up the Subscriber. Remember new on Subscriber should be done only once to avoid memory leaks.
// Set up a subscriber once
private Subscuber<Long> delaySubscriber = new Subscuber<Long> () {
#Override
public void onCompleted() {
//Wrap up things as onCompleted is called once onNext() is over
}
#Override
public void onError(Throwable e) {
//Keep an eye open for this. If onCompleted is not called, it means onError has been called. Make sure to override this method
}
#Override
public void onNext(Long aLong) {
// aLong will be from 0 to 1000
// Yuor code logic goes here
// If you want to run this code just once, just add a counter and call onComplete when the counter runs the first time
}
}
The snippet below will just emit the 1 in the onNext() of the subscriber.
Note that this is done on the Computation Threadpool created and managed by the RxJava library.
//Now when you want to start running your piece of cade, define an Observable interval that'll emit every second
private Observable<Long> runThisAfterDelay = Observable.just(1).delay(1000, TimeUnit.MILLISECONDS, Schedulers.computation());
// Subscribe to begin the emissions.
runThisAfterDelay.subscribe(delaySubscriber);
If you want to run a code after every one second, say, then you can do this:
private Observable<Long> runThisOnInterval = Observable.interval(1000, TimeUnit.MILLISECONDS, Schedulers.computation());
In addition to the first answer, you might want to consider what the API documentation says for the onReceive method:
[...] The function is normally called within the main thread of its process, so you should never perform long-running operations in it [...]
So it looks like generally it is not a good idea to start something that waits a couple of time within onReceive (even though, in your case it's less than the 10s limit).
I had a similar timinig problem with the BroadcastReceiver. I couldn't get my results processed even though I onReceive had been called with exactly what I was exepcting. It seemed that the thread the BroadastReceiver was running in, got killed before my result processing could finish. My solutuion was to kick off a new thread to perform all processing.
AlarmManager seems not to work very well for short periods of time like 10 seconds and according to user reports the behaviour heavily depends on the firmware.
At the end I decided to use Handler and Runnable in my service.
When creating the Handler, be sure to create it inside the Service class, not inside the BroadcastReceiver since in the last case you'll get Can't create Handler inside thread that has not called Looper.prepare()
public class NLService extends NotificationListenerService {
private NLServiceReceiver nlservicereciver;
Handler delayUpdateHandler = new Handler();
private Runnable runBroadcastUpdate;
public void triggerViewUpdate() {
/* Accumulate view updates for faster, resource saving operation.
Delay the update by some milliseconds.
And if there was pending update, remove it and plan new update.
*/
if (runBroadcastUpdate != null) {
delayUpdateHandler.removeCallbacks(runBroadcastUpdate);
}
runBroadcastUpdate = new Runnable() {
public void run() {
// Do the work here; execution is delayed
}
};
delayUpdateHandler.postDelayed(runBroadcastUpdate, 300);
}
class NLServiceReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
triggerViewUpdate();
}
}
}

Categories

Resources