kroegerama / android-kaiteki

A set of helper classes for modern android projects.
Apache License 2.0
8 stars 3 forks source link

Auto-retry when internet is evailable / cache surviving configuration changes? #1

Closed WingSafari5 closed 3 years ago

WingSafari5 commented 3 years ago

Can this library be used to @Retry an infinity amount of time until the server sends some sort of response (maybe 500 response allowed too)?

It would be very helpful to me if I could retry a call until it reaches the server. Ideally it would only retry when the OS transmits a status about available internet.
My goal is to cache "change requests" made via REST (e. g. change warehouse item amount from 7 to 5) and to retransmit them until the backend takes them.
It's not clear from the readme weather this is a goal or a non-goal.

I found out about this lib via this reddit thread.

kroegerama commented 3 years ago

The @Retry and @Cache annotations were deprecated and removed a while ago.

One way to repeat calls is to use the retrofitCall suspend function CoroutineHelper. You can pass a renewFun and a retryCount (set it to Int.MAX_VALUE - 1)

But in my opinion, the best way to achieve your goal is to write your own coroutine. Something like this (inside a ViewModel):

    fun makeCall(payload: Int): {
        viewModelScope.launch {
            while (coroutineContext.isActive) {
                retrofitCall {
                    MyApi.submitValue(payload)
                }.success {
                    //use `data` here

                    cancel()
                }.noSuccess {
                    when (code) {
                        500 -> TODO()
                        else -> TODO()
                    }
                }.error {
                    Timber.w(throwable)
                }
            }
        }
    }

The interface MyApi could be generated with OpenAPI Kgen. I wrote this generator to generate easy to use retrofit modules. Generated Example

Also, Kotlin Flows should work for you. Especially SharedFlow / MutableSharedFlow.

Another approach would be to use a worker and set the constraints to only run when a network connection is available. Workers will automatically retry if your return Result.retry() in your doWork() method. This would work best in combination with a database to store the result. You could observe the results with LiveData.

class MyWorker @WorkerInject constructor(
    @Assisted context: Context,
    @Assisted workerParams: WorkerParameters,
    private val repo: Repository
) : CoroutineWorker(context, workerParams) {

    override suspend fun doWork(): Result {
        Timber.d("doWork()")
        val success = repo.fetchAndStore()
        Timber.d("success: $success")
        return if (success) Result.success() else Result.retry()
    }

    companion object {
        fun enqueue(context: Context) {
            val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build()

            val work = PeriodicWorkRequestBuilder<MyWorker >(
                5, TimeUnit.MINUTES,
                1, TimeUnit.MINUTES
            )
                .addTag("my.worker")
                .setConstraints(constraints)
                .setBackoffCriteria(BackoffPolicy.LINEAR, 1, TimeUnit.MINUTES)
                .build()

            Timber.d("enqueue MyWorker: $constraints $work")
            WorkManager.getInstance(context).enqueueUniquePeriodicWork(Const.WORKER_NAME, ExistingPeriodicWorkPolicy.REPLACE, work)
        }
    }
}
WingSafari5 commented 3 years ago

Thank you for your excellent answer. So the answeris to write my own. Thanks! I was really hoping someone else had already written a retry lib which would survive restarts of the process...