StateFlow এবং SharedFlow

StateFlow এবং SharedFlow হলো ফ্লো এপিআই , যা ফ্লো-কে একাধিক কনজিউমারের কাছে সর্বোত্তমভাবে স্টেট আপডেট এবং ভ্যালু প্রেরণ করতে সক্ষম করে।

StateFlow

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

অ্যান্ড্রয়েডে, যে ক্লাসগুলোর একটি পর্যবেক্ষণযোগ্য ও পরিবর্তনযোগ্য স্টেট বজায় রাখার প্রয়োজন হয়, তাদের জন্য StateFlow একটি চমৎকার সমাধান।

Kotlin flow- এর উদাহরণ অনুসরণ করে, 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 এবং LiveData মধ্যে সাদৃশ্য রয়েছে। উভয়ই অবজার্ভেবল ডেটা হোল্ডার ক্লাস, এবং আপনার অ্যাপ আর্কিটেকচারে ব্যবহারের ক্ষেত্রে উভয়ই একটি অনুরূপ প্যাটার্ন অনুসরণ করে।

তবে মনে রাখবেন যে, StateFlow এবং LiveData ভিন্নভাবে কাজ করে:

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

shareIn ব্যবহার করে ঠান্ডা প্রবাহকে গরম করা

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

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

  • একটি 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

শেয়ার্ডফ্লো

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

আপনি shareIn ব্যবহার না করেও একটি SharedFlow তৈরি করতে পারেন। উদাহরণস্বরূপ, আপনি অ্যাপের বাকি অংশে টিক্স (ticks) পাঠাতে একটি 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 ফাংশনও রয়েছে।

অতিরিক্ত প্রবাহ সম্পদ