WorkManger in Android is executing doWork() more than once - android

I am using WorkManager to schedule some tasks but the problem is that work manager is executing those tasks { doWork() } more than once in a single call.
I am using:
'android.arch.work:work-runtime:1.0.0-alpha08'
I have tried using -alpha07,06,05,04. But I have same issue. Sometimes it even executes 5-6 times at once
Here is the code:
public class MyWorker extends Worker {
#NonNull
#Override
public Result doWork() {
Log.i("CountWorker","0");
sendNotification("Notice", "A notice was sent");
return Result.SUCCESS;
}
This is the Activity
public class MyWorkerActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final PeriodicWorkRequest pwr = new PeriodicWorkRequest
.Builder(MyWorker.class, 16, TimeUnit.MINUTES)
.setConstraints(Constraints.NONE)
.build();
WorkManager.getInstance().enqueue(pwr);
}
}
This is the result from Logcat:
09-24 16:44:35.954 22779-22816/com.simran.powermanagement I/CountWorker: 0
09-24 16:44:35.970 22779-22817/com.simran.powermanagement I/CountWorker: 0
09-24 16:44:35.977 22779-22818/com.simran.powermanagement I/CountWorker: 0

When you enqueue a PeriodicWorkRequest, that does not cancel any existing PeriodicWorkRequest that you have previously enqueued. Therefore as you have written your app, every time your activity starts, you add yet periodic work request, slowly going from 1 to 2 to 3 onward.
You instead want to use enqueueUniquePeriodicWork():
This method allows you to enqueue a uniquely-named PeriodicWorkRequest, where only one PeriodicWorkRequest of a particular name can be active at a time. For example, you may only want one sync operation to be active. If there is one pending, you can choose to let it run or replace it with your new work. The uniqueWorkName uniquely identifies this PeriodicWorkRequest.
With code such as:
final PeriodicWorkRequest pwr = new PeriodicWorkRequest
.Builder(MyWorker.class, 16, TimeUnit.MINUTES)
.setConstraints(Constraints.NONE)
.build();
WorkManager.getInstance().enqueueUniquePeriodicWork(
"my_worker",
ExistingPeriodicWorkPolicy.REPLACE,
pwr);

For OneTimeWorkRequest using version 1.0.0-beta01
WorkManager.getInstance()
.beginUniqueWork("Unique", ExistingWorkPolicy.KEEP, oneTimeWorkRequest)
.enqueue();
Cancel the existing sequence and REPLACE it with the new one.
KEEP the existing sequence and ignore your new request.
APPEND your new sequence to the existing one, running the new sequence's first task after the existing sequence's last task finishes
Official Documentation. https://developer.android.com/topic/libraries/architecture/workmanager/advanced

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 doWork getting fired twice

My goal is to run a simple task once an hour. At application start, I register an unique periodic work with ExistingPeriodicWorkPolicy.KEEP flag.
Here's my implementation:
public class MyWorker extends Worker {
public MyWorker(Context context, WorkerParameters workerParams) {
super(context, workerParams);
// I don't do anyting with workerParams
}
public static void enqueueWork() {
Constraints constraints = new Constraints.Builder()
.setRequiresBatteryNotLow(true)
.build();
PeriodicWorkRequest request = new PeriodicWorkRequest
.Builder(MyWorker.class, 1, TimeUnit.HOURS)
.setConstraints(constraints)
.addTag("MyWorkerWork")
.build();
WorkManager.getInstance().enqueueUniquePeriodicWork("MyWorkerWork",
ExistingPeriodicWorkPolicy.KEEP, request);
}
#NonNull
#Override
public Result doWork() {
Log.i(TAG, "doWork");
return Result.success();
}
}
The only place I call enqueueWork() is at Application#onCreate.
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
MyWorker.enqueueWork();
}
}
Above setup runs somewhat okay. One of the problems is that sometimes doWork is called twice in a row, with ~30 sec of time in between. This is not 100% reproducible but I see this often when I leave the phone overnight and come back next day and turn on the phone display for the first time.
Typical log would look like this:
01-13 13:29:04.601 19018 19449 I/MyApp: doWork
01-13 13:29:04.700 19018 19124 I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=37064d6c-297b-4c0d-9fd7-xxxxxxxxxxxx, tags={ com.xxx.MyWorker, MyWorkerWork } ]
01-13 13:29:42.203 19018 19482 I/MyApp: doWork
01-13 13:29:42.292 19018 19145 I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=37064d6c-297b-4c0d-9fd7-xxxxxxxxxxxx, tags={ com.xxx.MyWorker, MyWorkerWork } ]
The interesting thing is that this happens WITHOUT process getting killed. If you experienced this before, please help.

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.

How to manage 2 diffrent android periodic task in work manager

I want to schedule 2 different periodic task, for this I am using work manager.
1. Upload file to server - after every 20 min
2. Call API in - after every 15 min
For API call (Daily operation) following is my code:
PeriodicWorkRequest.Builder dailyWorkBuilder =
new PeriodicWorkRequest.Builder(CheckAccount.class, 15,
TimeUnit.MINUTES)
.setConstraints(new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build());
PeriodicWorkRequest DailyJob = dailyWorkBuilder.build();
WorkManager.getInstance().enqueueUniquePeriodicWork("DailyJob", ExistingPeriodicWorkPolicy.REPLACE,DailyJob);
To upload file I am using following code:
PeriodicWorkRequest.Builder wifiWorkBuilder =
new PeriodicWorkRequest.Builder(FileUpload.class, 20,
TimeUnit.MINUTES)
.setConstraints(new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build());
PeriodicWorkRequest wifiWork = wifiWorkBuilder.build();
WorkManager.getInstance().enqueueUniquePeriodicWork("wifiJob", ExistingPeriodicWorkPolicy.REPLACE,wifiWork);
Now here I am facing 2 difficulties:
1. If I open app - Lets say after open app my Activity is HomeActivity in this activity I had written dailyWorkBuilder code (which executes after every 20 min) will get called every time I open app. If I did not open app it will get called after 20 min but before 20 min I open app it gets called. So here I want to check if task is not running then only it should execute not every time when I open app
It also call wifiWorkBuilder (task which execute after every 15 min)it also get called every time when I open app. These 2 task are totally different and not depend on each other, but still if one task get called other will also get called before there specified time.
Whats wrong in above code. Any suggestion will be appreciated. Thanks
I had same problem some days ago. I managed that by-
Schedule work if Work-Manager is not scheduled already.
In your MainActivity where you set work.
if(isWorkScheduled("DailyJob")){
// now schedule DailyJob
}
I also asked a question and answered after getting solution.
private boolean isWorkScheduled(String tag) {
WorkManager instance = WorkManager.getInstance();
if (instance == null) return false;
LiveData<List<WorkStatus>> statuses = instance.getStatusesByTag(tag);
return statuses.getValue() != null && statuses.getValue().size() > 0;
}
This is up to you if you consider below method. It will return true
when some of its task is RUNNING or ENQUEUED.
private boolean isWorkScheduled(String tag) {
WorkManager instance = WorkManager.getInstance();
if (instance == null) return false;
LiveData<List<WorkStatus>> statuses = instance.getStatusesByTag(tag);
if (statuses.getValue() == null) return false;
boolean running = false;
for (WorkStatus workStatus : statuses.getValue()) {
running = workStatus.getState() == State.RUNNING | workStatus.getState() == State.ENQUEUED;
}
return running;
}
Suggestion
Always null check WorkManager object, because it can be null in some cases. You can see doc.
* #return The singleton instance of {#link WorkManager}; this may be {#code null} in unusual
* circumstances where you have disabled automatic initialization and have failed to
* manually call {#link #initialize(Context, Configuration)}.
Use ExistingPeriodicWorkPolicy.KEEP instead of ExistingPeriodicWorkPolicy.REPLACE, if your every task is important, work manager will auto start next work after its completion.

Android JobScheduler onStartJob called multiple times

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

Categories

Resources