web-dev-qa-db-fra.com

Kotlin Flow: Se désabonner de Sharedflow lorsque le fragment devient invisible

J'ai lu des sujets similaires mais je n'ai pas trouvé de réponse appropriée:

Dans ma classe Repository classe j'ai un rhume Flow que je veux partager à 2 Presenters/ViewModels _ Mon choix est d'utiliser shareIn Opérateur.

Jetons un coup d'œil sur Android Docs 'Exemple:

val latestNews: Flow<List<ArticleHeadline>> = flow {
    ...
}.shareIn(
    externalScope,  // e.g. CoroutineScope(Dispatchers.IO)?
    replay = 1,
    started = SharingStarted.WhileSubscribed()
)

Quels documents suggèrent que externalScope paramètre:

Un coroutinécope utilisé pour partager le flux. Cette portée devrait vivre plus longtemps que tout consommateur de maintenir le flux partagé en vie aussi longtemps que nécessaire.

Cependant, à la recherche de réponse sur la façon d'arrêter de souscrire un Flow, la réponse la plus votée au 2e lien dit:

Une solution n'est pas d'annuler le flux, mais la portée est lancée dans.

Pour moi, ces réponses sont contradictoires dans le cas SharedFlow. Et malheureusement, mon Presenter/ViewModel reçoit toujours les données les plus récentes, même après que son onCleared a été appelé.

Comment prévenir cela? C'est un exemple de savoir comment je consomme ce Flow dans mon Presenter/ViewModel:

fun doSomethingUseful(): Flow<OtherModel> {
    return repository.latestNews.map(OtherModel)

Si cela pourrait aider, j'utilise l'architecture MVI, alors doSomethingUseful réagit à certaines intention créées par l'utilisateur.

5
adek111

Utilisez SharedFlow. Dans l'exemple ci-dessous, je suis émettant une valeur d'un fragment et la collecte d'un autre.

ViewModel:

class MenuOptionsViewModel : ViewModel() {
private val _option = MutableSharedFlow<String>()
val option = _option.asSharedFlow()

suspend fun setOption(o : String){
    _option.emit(o)
}
}

Valeurs émettrices de fragments:

class BottomSheetOptionsFragment  : BottomSheetDialogFragment() , KodeinAware{

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    menuViewModel = activity?.run {
        ViewModelProviders.of(this).get(MenuOptionsViewModel::class.Java)
    } ?: throw Exception("Invalid Activity")

    listViewOptions.adapter = ArrayAdapter<String>(
        requireContext(),
        R.layout.menu_text_item,
        options
    )

    listViewOptions.setOnItemClickListener { adapterView, view, i, l ->
        val entry: String = listViewOptions.getAdapter().getItem(i) as String

// here we are emitting values
        GlobalScope.launch { menuViewModel.setOption(entry) }
        Log.d(TAG, "emitting flow $entry")
        dismiss()
    }
}
}

Fragment Collecte des valeurs:

class DetailFragment : BaseFragment(), View.OnClickListener, KodeinAware,
OnItemClickListener {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        menuViewModel = activity?.run {
            ViewModelProviders.of(this).get(MenuOptionsViewModel::class.Java)
        } ?: throw Exception("Invalid Activity")


// collecting values
                lifecycleScope.launchWhenStarted {
            menuViewModel.option.collect {
                Log.d(TAG, "collecting flow $it")
                
            }
        }
}
1
VIVEK CHOUDHARY