How to manage 2 diffrent android periodic task in work manager - android

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.

Related

Android workmanager minimum interval workaround

I am using the workmanager to run a service in the background, minimum interval from documentation is 15 minutes, but i would like to have an interval of 2 minutes.
I tried this code, it is working while debugging from android studio and runs properly every two minutes, but once i unplug the cable, or deploy it directly via apk, it runs only one time and stops.
In main activity onCreate/onResume :
OneTimeWorkRequest oneTimeRequest = new OneTimeWorkRequest.Builder(CallAndSmsWork.class).addTag(TAG).setInitialDelay(15, TimeUnit.SECONDS).build();
WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork("CallnSms", ExistingWorkPolicy.REPLACE, oneTimeRequest);
In the end of DoWork method of CallAndSmsWork service :
OneTimeWorkRequest oneTimeRequest = new OneTimeWorkRequest.Builder(CallAndSmsWork.class).setInitialDelay(120, TimeUnit.SECONDS).addTag(TAG).build();
WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork("CallnSms", ExistingWorkPolicy.REPLACE, oneTimeRequest);
Is there anything i did wrong, or any other option to have the service running properly every two minutes ?
P.S.: i am on android 10 , one plus 6t
For a 2 min interval you could use a periodic runnable, something like:
private val runnable = object : Runnable {
val periodInMillis = 2 * 60 * 1000L
override fun run() {
Log.d(DEBUG_TAG,"Runnable scheduled")
// Do stuff
mHandler?.postDelayed(this,periodInMillis)
}
}
And send it to a handler thread:
//Threading madness
private var mThread: HandlerThread? = null
private var mHandler: Handler? = null
Than you can initialize:
//Prepare Thread
if (mThread == null) {
mThread = HandlerThread("HeartBeatThread", Process.THREAD_PRIORITY_BACKGROUND)
mThread!!.start()
}
if (mHandler == null) mHandler =
Handler(mThread!!.looper) //Blocks until looper is prepared, which is fairly quick
mHandler!!.post(runnable)
In this way you have a task that enqueues itself and runs every 2 minutes. The issue with that could be Doze mode and something to do with the way that Android handles battery saver mode. For that you can pick up a lock I guess.

Execute function every full minute android/kotlin

Is this possible to execute function in android every minute starting from full minute, so eg. when I run my app at 8:30:12 the first function call will be at 8:31:00 and next at 8:32:00 and so on.
You have to first calculate the time required to become exact minute that is starting the task at 8:30:12, first call should happen at 8:31:00.
So for that you can pull current time using System.currentTimeMillis() and then ceil it's minute value to the nearest integer,
// import kotlin.math.ceil
val firstCallTime = ceil(System.currentTimeMillis() / 60_000.0).toLong() * 60_000
Then you can use coroutines to handle your task that will use a CommonPool to get reusable threads from and won't require to you to create threads.
// scope can be anything, for instance you want to call the function
// only when client is using your application then use viewModelScope provided,
// otherwise your own scope like `val scope = CoroutineScope(Dispatchers.Main)`
// Or use Dispatchers.Default for CPU intensive tasks
val parentJob = scope.launch {
// suspend till first minute comes after some seconds
delay(firstCallTime - System.currentTimeMillis())
while (true) {
launch {
yourFunctionToBeCalled()
}
delay(60_000) // 1 minute delay (suspending)
}
}
// If you ever want your job to cancel just do a cancel,
// the function delay suspends and so cancellable without interrupting the threads
// parentJob.cancel()
Use PeriodicWorkRequest along with WorkManager class in android. It goes like this:
val work = PeriodicWorkRequestBuilder<MyWorker>(60, TimeUnit.SECONDS)
.build();
val workManager = WorkManager.getInstance(context);
workManager.enqueuePeriodicWork(work);

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

WorkManger in Android is executing doWork() more than once

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

How to check if Async Task is already running

I have an app that needs to do an intensive database operation on start up. The app holds a local copy of the contacts on the phone and synchronizes with the android contact database on startup.
If a user starts the app, an Async Task is started that does the database synch in the background. If the user closes the app, the operation continues running which is fine. However if the user opens the app again, the Async Task is started and an error is produced.
Is there anyway of checking if the Task is already running from a different instance of the app?
Use getStatus() to get the status of your AsyncTask. If status is AsyncTask.Status.RUNNING then your task is running.
EDIT: you should reconsider your implementation and hold the AsyncTask probably in a Service or IntentService to fetch your data from the web.
I've managed to handle this problem with some sort of Singleton pattern.
Hope it helps.
// fill the places database from a JSON object
public class myAsyncTask extends AsyncTask<Void,Integer,Integer> {
Activity mContext = null;
static AsyncTask<Void,Integer,Integer> myAsyncTaskInstance = null;
// Private Constructor: can't be called from outside this class
private myAsyncTask(Activity iContext) {
mContext = iContext;
}
public static AsyncTask<Void, Integer, Integer> getInstance(Activity iContext) {
// if the current async task is already running, return null: no new async task
// shall be created if an instance is already running
if (myAsyncTaskInstance != null && myAsyncTaskInstance.getStatus() == Status.RUNNING) {
// it can be running but cancelled, in that case, return a new instance
if (myAsyncTaskInstance.isCancelled()) {
myAsyncTaskInstance = new myAsyncTask(iContext);
} else {
// display a toast to say "try later"
Toast.makeText(iContext, "A task is already running, try later", Toast.LENGTH_SHORT).show();
return null;
}
}
//if the current async task is pending, it can be executed return this instance
if (myAsyncTaskInstance != null && myAsyncTaskInstance.getStatus() == Status.PENDING) {
return myAsyncTaskInstance;
}
//if the current async task is finished, it can't be executed another time, so return a new instance
if (myAsyncTaskInstance != null && myAsyncTaskInstance.getStatus() == Status.FINISHED) {
myAsyncTaskInstance = new myAsyncTask(iContext);
}
// if the current async task is null, create a new instance
if (myAsyncTaskInstance == null) {
myAsyncTaskInstance = new myAsyncTask(iContext);
}
// return the current instance
return myAsyncTaskInstance;
}
#Override
protected Integer doInBackground(Void... iUnUsed) {
// ...
}
}
I think you should check the concept of Application in Android.
http://developer.android.com/reference/android/app/Application.html
In fact there is no such thing as
different instance of the app
. The Application is always the same for all your Activities/Services.
That means that you'd left the Activity and opened it again, 2 cases are possible:
The system already killed your application. In this case AsyncTask is dead already and it's safe to start a new one
The Application was still alive, so AsyncTask possibly still running.
In 2nd case I will recommend to use some static variables, pointing to this AsyncTask or it's state. If your app was still alive when 2nd time opened - all static references will be still valid, so you can successfully operate.
PS: By the way, in current approach be aware that your application can be terminated by the system at any time. So AsyncTask can be interrupted in any moment. It it's not ok for you - please check IntentServices - components, specially designed for background-operation purpose. http://developer.android.com/reference/android/app/IntentService.html
Good luck!

Categories

Resources