Is there a way to make sure all Intents are execut...
# mvikotlin
z
Is there a way to make sure all Intents are executed when the Fragment is being destroyed? I have to do some analytics tracking when the Fragment is paused. My Fragment is in a ViewPager, and on some devices there are no more than 2 milliseconds to send the intent before everything is destroyed, but I have to do a little calculation as well, which is enough to miss it sometimes. What I did for now is that I copied the code from com.arkivanov.mvikotlin.extensions.coroutines.Binder.kt, and added a delay to the BuilderBinder.stop function like this:
Copy code
override fun stop() {
        GlobalScope.launch(mainContext) {
            delay(DELAY_STOP)
            job?.cancel()
            job = null
        }
    }
First of all, is this correct? Is this the best I can do? Maybe, does it make sense to add this to the library?
a
I usually just don't dispose such an operation. E.g. with Rx I just call
subscribe
and don't remember the Disposable. And also put a descriptive comment. With coroutines, perhaps this would by launching the job in the
GlobalScope
. Delaying the Destroy even sounds also interesting. Most likely I would create an extensions as follows:
fun Lifecycle.delayedDestroy(millis: Long): Lifecycle = TODO()
z
Maybe I missed out an important information. I send the Intent because I want to use a dependency that I don’t want in the Fragment. So the Store needs to be used, but it’s stopped by the lifecycle. Not sure if the extension on the LifeCycle can avoid this?
a
You should be able to substitute the lifecycle with a delayed one, I guess.
Ahh indeed, if the Store is created via InstanceKeeper, the delayed lifecycle won't help
Are using InstanceKeeper?
Btw, there is another option. You can override
dispose
method in the
Executor
and call
super.dispose()
after a certain delay.
I guess the following should work:
Copy code
override fun dispose() {
            scope.launch { 
                delay(1000L)
                super.dispose()
            }
        }
z
I don’t use InstanceKeeper yet. I will try delaying dispose. Thanks for your help
👍 1
The delay in dispose did not work unfortunately. I do the calculation, or a single call really in the Fragment. I need to track the percentage scrolled in a ViewPager item. By the time I dispatch the Intent two milliseconds later, the binding is already stopped.
a
In this case you should delay the store disposal. Could you show the related code? E.g. where store.dispose() is called.
z
It’s simply in a ViewModel:
Copy code
override fun onCleared() {
    topicStore::dispose
}
But I already tried that. The binding is already stopped when this is called.
a
In this case you could delay right there, e.g. using GlobalScope.
z
Sorry, I edited my post. ☝️
I tried to add a delay with GlobalScope
a
First of all, looks like there is a mistake in the last snippet -
topicStore::dispose
is just a method reference, it doesn't actually call
dispose
. You could use the following to delay the disposal of the store:
Copy code
override fun onCleared() {
    GlobalScope.launch {
        delay(DELAY_STOP)
        topicStore.dispose()
    }
}
z
Ah, rookie mistake. It means that I have to add delay to the store disposal too. But it doesn’t mean I can avoid the delay in the stop function.
a
Yeah, binding depends on Lifecycle, so the whole Lifecycle could be wrapped with a delayed one.
z
I’m not sure how to wrap the lifecycle, sorry. Could you give me an example, please?
a
Sure I can help! I guess the following should work, but this is just from the top of my head.
Copy code
fun Lifecycle.delayedDestroy(millis: Long): Lifecycle {
    val lifecycle = LifecycleRegistry()

    when (state) {
        Lifecycle.State.CREATED -> lifecycle.create()
        Lifecycle.State.STARTED -> lifecycle.start()
        Lifecycle.State.RESUMED -> lifecycle.resume()
        Lifecycle.State.DESTROYED -> {
            lifecycle.create()
            lifecycle.destroy()
        }
        Lifecycle.State.INITIALIZED -> Unit
    }

    subscribe(
        object : Lifecycle.Callbacks by lifecycle {
            override fun onDestroy() {
                GlobalScope.launch {
                    delay(millis)
                    lifecycle.onDestroy()
                }
            }
        }
    )

    return lifecycle
}
z
Works like a charm. Thanks again!
🎉 1
a
The snippet was incomplete. If the original lifecycle is already in some state (e.g. RESUMED), the wrapped lifecycle would stay INITIALIZED. And this uncovered one point to improve - to add
initialState
argument to
LifecycleRegistry(...)
function - filed https://github.com/arkivanov/Essenty/issues/56. I have updated the snippet here
👍 1