Android generalized function for state flow in fragment

Viewed 1588

I am using StateFlow in my app and in my Fragment I use this to -

private var job: Job? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        job = lifecycleScope.launchWhenResumed {
            viewModel.getData().collect {
                // ...
            }
        }
    }

    override fun onPause() {
        job?.cancel()
        super.onPause()
    }

As you see I cancel the job in onPause. How could I use a generalized function so that I can avoid doing the job?.cancel in every fragment.

I prefer not to use a BaseFragment

3 Answers

A simple solution would be to utilize the fragments lifecycle to automatically cancel the job when it is paused.

fun CoroutineScope.launchUntilPaused(lifecycleOwner: LifecycleOwner, block: suspend CoroutineScope.() -> Unit){
    val job = launch(block = block)
    lifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
        override fun onPause(owner: LifecycleOwner) {
            job.cancel()
            lifecycleOwner.lifecycle.removeObserver(this)
        }
    })
}

//Usage
class MyFragment: Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        lifecycleScope.launchUntilPaused(this){
            someFlow.collect{
                ...
            }
        }
    }
}

If you have many of these jobs per fragment, I would advice to use a custom CoroutineScope instead, to avoid having many lifecycle observers active.

class CancelOnPauseScope(lifecycleOwner: LifecycleOwner): CoroutineScope by MainScope(){
    init{
        lifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver{
            override fun onPause(owner: LifecycleOwner) {
                cancel()
                lifecycleOwner.lifecycle.removeObserver(this)
            }
        })
    }
}

class MyFragment: Fragment() {
    private val scope = CancelOnPauseScope(this)
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        scope.launch{
            someFlow.collect{
                ...
            }
        }
    }
}

a new way:

   // Start a coroutine in the lifecycle scope
    lifecycleScope.launch {
        // repeatOnLifecycle launches the block in a new coroutine every time the
        // lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
        repeatOnLifecycle(Lifecycle.State.STARTED) {
        }
     }
public class AnyOfYourFragments extends AbsractFragment{

   //you do here what you want to do
   
}

And in your AbstractFragment:

public abstract class AbstractFragment extends Fragment{
private var job: Job? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        job = lifecycleScope.launchWhenResumed {
            viewModel.getData().collect {
                // ...
            }
        }
    }

    override fun onPause() {
        job?.cancel()
        super.onPause()
    }
}

I don't know kotlin, so my code is kind of mix with java but i'm sure you got the idea

Related