8

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?
Igor Escodro
  • 1,307
  • 2
  • 16
  • 30

2 Answers2

5
  • Yes WorkManager is a good solution(even could be the best one)
  • You should use RxWorker instead of Worker. here is an example:

    1. To implement it. add androidx.work:work-rxjava2:$work_version to your build.gradle file as dependency.

    2. 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 :

sma6871
  • 3,198
  • 3
  • 38
  • 52
  • But this only works with a single that emits one value. What if you want to use an Observable that keeps emitting until a job is done? (like a download progress bar) – My House Jan 24 '20 at 18:00
  • @MyHouse This `Single` is used to tell WorkManeger that my job is done so dispose resources. For download case, you could use Flowable(or Observable) then map results in onComplete or onError events, moreover, you should not return anything in onNext event. – sma6871 Jan 25 '20 at 08:37
-1

You can clear it in onStoped() method then compositeDisposable.dispose();

Then call super.onStoped()

EslamWael74
  • 443
  • 4
  • 10
  • There is no `onStop()` method in `Worker` class and the method `onStopped()` that I'm already implemented it is never called. – Igor Escodro Oct 17 '18 at 11:15
  • it's calling when a work finished and the Android OS knows when work finished but another solution you can make your calls in another class and dispose your work when you need – EslamWael74 Oct 18 '18 at 14:19
  • you can create another class to make your implementation not use CompositeDisposable in Worker Class :D – EslamWael74 Oct 22 '18 at 14:12