StateFlow ও SharedFlow

StateFlow ও SharedFlow হল Flow API যা একাধিক কনজিউমারদের কাছে অপ্টিমালি স্টেট আপডেট ও ভ্যালু ইমিট করার জন্য ফ্লোকে সক্ষম করে।

StateFlow

StateFlow হল একটি স্টেট-হোল্ডার অবজার্ভেবল ফ্লো যা এর কালেক্টরদের কাছে বর্তমান ও নতুন স্টেট আপডেট ইমিট করে। এছাড়াও, এর value প্রপার্টির মাধ্যমে বর্তমান স্টেট ভ্যালু পড়া যেতে পারে। ফ্লোতে স্টেট আপডেট করে পাঠাতে, MutableStateFlow ক্লাসের value প্রপার্টিতে নতুন ভ্যালু অ্যাসাইন করুন।

Android-এ, StateFlow হল সেইসব ক্লাসের জন্য উপযুক্ত যেগুলিকে পরিবর্তনযোগ্য স্টেট বজায় রাখতে হয়।

Kotlin flows থেকে পাওয়া উদাহরণ অনুযায়ী, LatestNewsViewModel থেকে StateFlow এক্সপোজ করা যেতে পারে যাতে View UI স্টেট আপডেট শুনতে এবং কনফিগারেশন পরিবর্তন হলেও স্ক্রিন স্টেটকে অক্ষত রাখতে পারে।

class LatestNewsViewModel(
    private val newsRepository: NewsRepository
) : ViewModel() {

    // Backing property to avoid state updates from other classes
    private val _uiState = MutableStateFlow(LatestNewsUiState.Success(emptyList()))
    // The UI collects from this StateFlow to get its state updates
    val uiState: StateFlow<LatestNewsUiState> = _uiState

    init {
        viewModelScope.launch {
            newsRepository.favoriteLatestNews
                // Update UI with the latest favorite news
                // Writes to the value property of MutableStateFlow,
                // adding a new element to the flow and updating all
                // of its collectors
                .collect { favoriteNews ->
                    _uiState.value = LatestNewsUiState.Success(favoriteNews)
                }
        }
    }
}

// Represents different states for the LatestNews screen
sealed class LatestNewsUiState {
    data class Success(val news: List<ArticleHeadline>) : LatestNewsUiState()
    data class Error(val exception: Throwable) : LatestNewsUiState()
}

MutableStateFlow আপডেট করার জন্য দায়ী ক্লাস হল প্রডিউসার, এবং StateFlow থেকে সংগ্রহ করা সব ক্লাস হল কনজিউমার। flow বিল্ডার ব্যবহার করে তৈরি করা কোল্ড ফ্লোয়ের বিপরীতে, StateFlow হল হট: ফ্লো থেকে সংগ্রহ করা ডেটা কোনও প্রোডিউসার কোড ট্রিগার করে না। StateFlow সবসময় অ্যাক্টিভ থাকে এবং মেমরিতে থাকে এবং এটি তখনই গার্বেজ কালেকশনের উপযুক্ত হয় যখন গার্বেজ কালেকশন রুট থেকে এটির কোনও রেফারেন্স থাকে না।

কোনও নতুন কনজিউমার যখন ফ্লো থেকে ডেটা সংগ্রহ করা শুরু করে, তখন সেটি স্ট্রিমের শেষ স্টেট এবং তার পরবর্তী যেকোনও স্টেট পায়। আপনি এই আচরণ অন্যান্য পর্যবেক্ষণযোগ্য ক্লাসে দেখতে পাবেন, যেমন LiveData।

অন্যান্য ফ্লোয়ের মতো View StateFlow-এর জন্য অপেক্ষা করে:

class LatestNewsActivity : ComponentActivity() {
    private val latestNewsViewModel: LatestNewsViewModel = TODO() // getViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        // ...
        // 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) {
                // Trigger the flow and start listening for values.
                // Note that this happens when lifecycle is STARTED and stops
                // collecting when the lifecycle is STOPPED
                latestNewsViewModel.uiState.collect { uiState ->
                    // New value received
                    when (uiState) {
                        is LatestNewsUiState.Success -> showFavoriteNews(uiState.news)
                        is LatestNewsUiState.Error -> showError(uiState.exception)
                    }
                }
            }
        }
    }
}

যেকোনও ফ্লোকে StateFlow-এ কনভার্ট করতে, stateIn ইন্টারমিডিয়েট অপারেটর ব্যবহার করুন।

StateFlow, Flow ও LiveData

StateFlow ও LiveData-এর মধ্যে কিছু মিল আছে। দুটিই হল অবজার্ভেবল ডেটা হোল্ডার ক্লাস এবং আপনার অ্যাপ আর্কিটেকচারে ব্যবহার করা হলে, দুটিই একই ধরনের প্যাটার্ন অনুসরণ করে।

তবে, মনে রাখবেন যে StateFlow এবং LiveData আলাদা আলাদা আচরণ করে:

  • StateFlow-এর কনস্ট্রাক্টরে একটি প্রাথমিক স্টেট পাস করতে হয়, কিন্তু LiveData-এর ক্ষেত্রে তা করতে হয় না।
  • LiveData.observe() ভিউ STOPPED স্টেটে গেলে অটোমেটিক গ্রাহককে আনরেজিস্টার করে দেয়, যেখানে StateFlow বা অন্য কোনও ফ্লো থেকে ডেটা সংগ্রহ করলে তা অটোমেটিক বন্ধ হয় না। একই ধরনের বিহেভিয়ার পেতে, আপনাকে Lifecycle.repeatOnLifecycle ব্লক থেকে ফ্লো সংগ্রহ করতে হবে।

shareIn ব্যবহার করে কোল্ড ফ্লোকে হট ফ্লোতে পরিবর্তন করা

StateFlow হল একটি হট ফ্লো—ফ্লোটি যতক্ষণ না সংগ্রহ করা হচ্ছে বা যতক্ষণ না গার্বেজ কালেকশন রুট থেকে এর কোনও রেফারেন্স আছে ততক্ষণ এটি মেমরিতে থাকে। আপনি shareIn অপারেটর ব্যবহার করে কোল্ড ফ্লোকে হট ফ্লোতে পরিবর্তন করতে পারবেন।

Kotlin ফ্লো-তে তৈরি callbackFlow-কে উদাহরণ হিসেবে ব্যবহার করে, প্রতিটি কালেক্টরকে নতুন ফ্লো তৈরি করতে না দিয়ে, আপনি shareIn ব্যবহার করে কালেক্টরদের মধ্যে Firestore থেকে পাওয়া ডেটা শেয়ার করতে পারেন। আপনাকে নিম্নলিখিত তথ্য দিতে হবে:

  • ফ্লো শেয়ার করার জন্য ব্যবহৃত CoroutineScope। এই স্কোপকে যেকোনও গ্রাহকের চেয়ে বেশি সময় ধরে চালু থাকতে হবে, যাতে শেয়ার করা ফ্লোকে যতক্ষণ প্রয়োজন ততক্ষণ চালু রাখা যায়।
  • প্রতিটি নতুন সংগ্রহকারীকে রিপ্লে করার জন্য আইটেমের সংখ্যা।
  • স্টার্ট আচরণ সংক্রান্ত নীতি।

class NewsRemoteDataSource(
    private val externalScope: CoroutineScope
) {
    val latestNews: Flow<List<ArticleHeadline>> = flow {
        // ...
    }.shareIn(
        externalScope,
        replay = 1,
        started = SharingStarted.WhileSubscribed()
    )
}

এই উদাহরণে, latestNews ফ্লো নতুন কালেক্টরকে সর্বশেষ নির্গত আইটেমটি আবার প্লে করে এবং externalScope চালু থাকাকালীন এবং অ্যাক্টিভ কালেক্টর থাকাকালীন অ্যাক্টিভ থাকে। SharingStarted.WhileSubscribed() স্টার্ট নীতি চালু থাকলে, অ্যাক্টিভ সাবস্ক্রাইবার থাকাকালীন আপস্ট্রিম প্রডিউসার অ্যাক্টিভ থাকে। অন্যান্য স্টার্ট নীতি উপলভ্য, যেমন SharingStarted.Eagerly প্রোডিউসার সাথে সাথেই শুরু করতে অথবা SharingStarted.Lazily প্রথম সাবস্ক্রাইবার আসার পরে শেয়ার করা শুরু করতে এবং ফ্লো সবসময় অ্যাক্টিভ রাখতে।

SharedFlow

shareIn ফাংশন একটি SharedFlow রিটার্ন করে, এটি একটি হট ফ্লো যা এর থেকে সংগ্রহ করা সমস্ত কনজিউমারকে ভ্যালু এমিট করে। SharedFlow হল StateFlow-এর খুব বেশি কনফিগার করা যায় এমন সাধারণীকরণ।

আপনি shareIn ব্যবহার না করেই SharedFlow তৈরি করতে পারবেন। যেমন, আপনি অ্যাপের বাকি অংশকে টিক পাঠানোর জন্য SharedFlow ব্যবহার করতে পারেন, যাতে সব কন্টেন্ট একই সময়ে নির্দিষ্ট সময় অন্তর রিফ্রেশ হয়। লেটেস্ট খবর পাওয়ার পাশাপাশি, আপনি ব্যবহারকারীর তথ্য বিভাগকেও তার পছন্দের বিষয়ের সংগ্রহ দিয়ে রিফ্রেশ করতে পারেন। নিচের কোড স্নিপেটে, একটি TickHandler একটি SharedFlow এক্সপোজ করে যাতে অন্যান্য ক্লাস জানতে পারে যে কখন এর কন্টেন্ট রিফ্রেশ করতে হবে। StateFlow-এর মতো, ফ্লোতে আইটেম পাঠানোর জন্য কোনও ক্লাসে MutableSharedFlow ধরনের ব্যাকিং প্রপার্টি ব্যবহার করুন:

// Class that centralizes when the content of the app needs to be refreshed
class TickHandler(
    private val externalScope: CoroutineScope,
    private val tickIntervalMs: Long = 5000
) {
    // Backing property to avoid flow emissions from other classes
    private val _tickFlow = MutableSharedFlow<Unit>(replay = 0)
    val tickFlow: SharedFlow<Unit> = _tickFlow

    init {
        externalScope.launch {
            while (true) {
                _tickFlow.emit(Unit)
                delay(tickIntervalMs)
            }
        }
    }
}

class NewsRepository(
    // ...
    private val tickHandler: TickHandler,
    private val externalScope: CoroutineScope
) {
    init {
        externalScope.launch {
            // Listen for tick updates
            tickHandler.tickFlow.collect {
                refreshLatestNews()
            }
        }
    }

    suspend fun refreshLatestNews() { /* ... */ }
    // ...
}

আপনি নিম্নলিখিত উপায়ে SharedFlow আচরণ কাস্টমাইজ করতে পারবেন:

  • replay আপনাকে নতুন সাবস্ক্রাইবারদের জন্য আগে প্রকাশিত একাধিক ভ্যালু আবার পাঠানোর অনুমতি দেয়।
  • onBufferOverflow আপনাকে এমন একটি নীতি নির্দিষ্ট করতে দেয়, যখন পাঠানোর জন্য আইটেম দিয়ে বাফার ভর্তি হয়ে যায়। ডিফল্ট ভ্যালু হল BufferOverflow.SUSPEND, যা কলকারীকে সাসপেন্ড করে দেয়। অন্যান্য বিকল্প হল DROP_LATEST বা DROP_OLDEST।

MutableSharedFlow-এর একটি subscriptionCount প্রপার্টিও আছে যাতে অ্যাক্টিভ কালেক্টরের সংখ্যা থাকে, এর ফলে আপনি সেই অনুযায়ী নিজের ব্যবসার লজিক অপ্টিমাইজ করতে পারেন। MutableSharedFlow-এ resetReplayCache ফাংশনও আছে, যদি আপনি ফ্লোতে পাঠানো লেটেস্ট তথ্য আবার চালাতে না চান।

ফ্লো সংক্রান্ত অতিরিক্ত রিসোর্স