I just search for code that let me do an action after the timer (with specific date and time) finish (in Kotlin) and save it in a list
Like timer for post a tweet on Twitter:
https://business.twitter.com/en/help/campaign-editing-and-optimization/scheduled-tweets.html
You can use WorkManager for that.
Dependency:
implementation "androidx.work:work-runtime-ktx:2.3.0"
Example:
class LogWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
override fun doWork(): Result {
// Do the work here--in this case, upload the images.
Log.i("ToastWorker", "doWork: Working ⚒ ⚒ ⚒")
// Indicate whether the task finished successfully with the Result
return Result.success()
}
}
Then set the delay time
val logWorkRequest = OneTimeWorkRequestBuilder<LogWorker>()
.setInitialDelay(5, TimeUnit.SECONDS) // here you can set the delay time in Minutes, Hours
.build()
Start the timer
WorkManager.getInstance(this).enqueue(toastWorkRequest)
Here is a Codelab for more insight. You can also read more here
Related
I am using Workmanager to execute a task within a time period of minutes but it gets executed for the first time only. From my point of view it should execute every minutes.
I am testing on device while the app is in foreground running and power is on.
Code:
class MainActivity : AppCompatActivity() {
val TAG: String = "MainActivity"
lateinit var workLiveData: LiveData<List<WorkInfo>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initWM()
}
private fun initWM() {
val request = PeriodicWorkRequestBuilder<DemoWorker>(1, TimeUnit.MINUTES)
.addTag(TAG)
.build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(TAG,
ExistingPeriodicWorkPolicy.REPLACE, request)
}
}
DemoWorker:
class DemoWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
val TAG: String = "MainActivity"
override fun doWork(): Result {
Log.d(TAG, "doWork: ")
return try {
Result.success(workDataOf("KEY" to "SUCCESS"))
} catch (e: Exception) {
Result.failure()
}
}
}
A reminder about the “minimal interval”. WorkManager is balancing two different requirements: the application with its WorkRequest, and the Android operating system with its need to limit battery consumption. For this reason, even if all the constraints set on a WorkRequest are satisfied, your Work can still be run with some additional delay.
So you are replacing one work after another. The OS may not have the proper time to execute the work. So the best option will be to try with a 1-hour delay.
You can use a flexInterval.Let’s look at an example. Imagine you want to build a periodic Work request with a 30 minutes period. You can specify a flexInterval, smaller than this period, say a 15 minute flexInterval.
The actual code to build a PeriodicWorkPequest with this parameters is:
val logBuilder = PeriodicWorkRequestBuilder<MyWorker>(
30, TimeUnit.MINUTES,
15, TimeUnit.MINUTES)
The result is that our worker will be executed in the second half of the period (the flexInterval is always positioned at the end of the repetition period):
What is a safe and greater method to execute tasks ( in my case call a function ) in specific time ( in my case every two days ) ?
The function retrieves data from the Web. My target is update the data in app whenever it changes on the webserver.
Thanks, good job!
1- If you don't need to be exact about time, you need to use WorkManager and set periodic work request
https://developer.android.com/topic/libraries/architecture/workmanager
fun CreateWorkRequest()
{
val workManager = WorkManager.getInstance(context)
val duration = 2*24*60*60*1000L //2 days in mili second
val workRequest = PeriodicWorkRequestBuilder<AutoBackUpWorker>(duration, TimeUnit.MILLISECONDS)
.setInitialDelay(duration/2, TimeUnit.MILLISECONDS)
.addTag("diaryBackUpWork")
.build()
workManager.enqueueUniquePeriodicWork("backupwork", ExistingPeriodicWorkPolicy.REPLACE, autoBackUpRequest)
}
Worker Class:
class RequestedWorker(val appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams)
{
override fun doWork()
{
}
}
2- If you need to be exact about time (like at 8pm everyday) you need to use Alarm Manager
3- In your case the best choice is implementing DownloadManager for download request in the doWork() method
I want to schedule my MainActivty to perform checks on its data periodically without showing UI to the user. When a condition is true, I want to show a notification.
Furthermore, I need to check for this notification only once per day, without an exact timing and I want this check to remain scheduled even if the device is rebooted /after poweron.
Looks like the worker model with TimeUnit.ONE_DAY is the best fit for me (Here I am setting the interval at one minute so I don't need to wait for one day to test).
I create in "onCreate" method the Periodic Request Builder and the enqueue the request in the work manager.
Outside onCreate method I define the sendNotification method and provide my class implementatio on Notification Worker, where I define the action to be performed when the woker callback is fired.
In main class I have:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
[...]
val CheckRequest = PeriodicWorkRequestBuilder<NotificationWorker>(1, TimeUnit.MINUTES).build()
WorkManager.getInstance(context).enqueue(CheckRequest)
}
fun sendNotification(){
with(NotificationManagerCompat.from(this)) {
notify(notificationId, builder.build())
}
class NotificationWorker(appContext: Context, workerParams: WorkerParameters):
Worker(appContext, workerParams) {
override fun doWork(): Result {
// Do the work here--in this case, upload the images.
MainActivity().sendNotification()
// Indicate whether the work finished successfully with the Result
return Result.success()
}
}
}
My Issue is that I encounter the following error
java.util.concurrent.ExecutionException: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
I think that what's happening is that I am calling a method from the super class of the worker when the UI is not active, so the app context etc is not availlable to the called method.
Any hin on this point would be gladly appreciated.
Kind regards,
I am using workmanager to upload image file to server and want to pass uploaded imagepath to next worker but Result.success(output) gets called before upload function complete.
class UploadImageWorker(context: Context, workerParameters: WorkerParameters) : RxWorker(context, workerParameters) {
override fun createWork(): Single<Result> {
return Single.fromCallable {
//UploadImageFile()
//updating output
}.map{
Result.success(output)
}
}
}
Success, in this case, is only reporting that the job fired successfully, it is not a measure of the work itself.
You should just use the worker to hand off the work and do something like:
MyFactory.ImageFactory.UploadImageFile(file)
Then inside your ImageFactory use Observer pattern or something similar to subscribe to the state of your UploadImageFile. This would be the true measure of success.
You should use RxWorker. You can return a Single<Result>. Read the documentation for RxWorker for more information.
https://developer.android.com/reference/androidx/work/RxWorker
I implemented an AlarmManager to send notifications when user adds a due date to a Task. However, when the user turns off the device, all the alarms are lost. Now I'm updating the BroadcastReceiver to receive an android.intent.action.BOOT_COMPLETED and reschedule all the alarms set to each task.
My first attempt was to get an Rx Single with all the tasks where the due date is higher than the current time inside the BroadcastReceiver, then reschedule all the alarms. The issue is I'm not able to dispose the Observable once the BroadcastReceiver has no lifecycle. Also, it seems that this is not a good approach.
During my researches, the IntentService was a good solution for this case, but I'm getting into the new WorkManager library and the OneTimeWorkRequest looks like a good and simple solution.
The Worker is being called and executing correctly, but I'm not able to dispose the Observable because the onStopped method is never called.
Here is the implementation, based on this snippet:
class TaskAlarmWorker(context: Context, params: WorkerParameters) :
Worker(context, params), KoinComponent {
private val daoRepository: DaoRepository by inject()
private val compositeDisposable = CompositeDisposable()
override fun doWork(): Result {
Timber.d("doWork")
val result = LinkedBlockingQueue<Result>()
val disposable =
daoRepository.getTaskDao().getAllTasks().applySchedulers().subscribe(
{ result.put(Result.SUCCESS) },
{ result.put(Result.FAILURE) }
)
compositeDisposable.add(disposable)
return try {
result.take()
} catch (e: InterruptedException) {
Result.RETRY
}
}
override fun onStopped(cancelled: Boolean) {
Timber.d("onStopped")
compositeDisposable.clear()
}
}
Is WorkManager a good solution for this case?
Is it possible to dispose the Observable correctly?
Yes WorkManager is a good solution(even could be the best one)
You should use RxWorker instead of Worker. here is an example:
To implement it. add androidx.work:work-rxjava2:$work_version to your build.gradle file as dependency.
Extend your class from RxWorker class, then override createWork() function.
class TaskAlarmWorker(context: Context, params: WorkerParameters) :
RxWorker(context, params), KoinComponent {
private val daoRepository: DaoRepository by inject()
override fun createWork(): Single<Result> {
Timber.d("doRxWork")
return daoRepository.getTaskDao().getAllTasks()
.doOnSuccess { /* process result somehow */ }
.map { Result.success() }
.onErrorReturn { Result.failure() }
}
}
Important notes about RxWorker:
The createWork() method is called on the main thread but returned single is subscribed on the background thread.
You don’t need to worry about disposing the Observer since RxWorker will dispose it automatically when the work stops.
Both returning Single with the value Result.failure() and single with an error will cause the worker to enter the failed state.
You can override onStopped function to do more.
Read more :
How to use WorkManager with RxJava
Stackoverflow answer
You can clear it in onStoped() method then compositeDisposable.dispose();
Then call super.onStoped()