StateFlow — সারমর্ম, Android এ StateFlow vs LiveData

লেখক: IT Sectr প্রকাশিত: 2026-02-20 পড়ার সময়: 9 মিনিট

StateFlow — Kotlin Coroutines লাইব্রেরি থেকে একটি রিঅ্যাকটিভ স্টেট কন্টেইনার, যা StateFlow<T> উপস্থাপন করে — Flow এর একটি উপপ্রকার যা সর্বদা বর্তমান মান সংরক্ষণ করে এবং নতুন সাবস্ক্রাইবারদের কাছে এটি নির্গত করে। আমরা StateFlow এর সারমর্ম ব্যাখ্যা করি: LiveData এর বিপরীতে, StateFlow Android ফ্রেমওয়ার্কের সাথে আবদ্ধ নয় এবং যেকোনো Kotlin প্ল্যাটফর্মে কাজ করে। Google (Android Developers, 2025) অনুসারে, বিশুদ্ধ Kotlin এ নতুন প্রকল্পের জন্য StateFlow কে LiveData এর প্রাথমিক বিকল্প হিসাবে সুপারিশ করা হয়, বিশেষ করে Jetpack Compose এর সাথে MVVM আর্কিটেকচারে।

মূল পয়েন্ট

  • StateFlow — kotlinx.coroutines.flow থেকে একটি স্টেট হোল্ডার, সর্বদা একটি বর্তমান মান সংরক্ষণ করে এবং সাবস্ক্রিপশনে এটি নির্গত করে।
  • MutableStateFlow — mutable value প্রপার্টি সহ একটি পরিবর্তনযোগ্য StateFlow, ViewModel এর ভিতরে ব্যবহৃত হয় এবং StateFlow হিসাবে প্রকাশিত হয়।
  • collect() — পরিবর্তনগুলিতে সাবস্ক্রাইব করার জন্য Flow এর একটি টার্মিনাল অপারেটর; UI এর জন্য Compose এ collectAsState() বা View এ repeatOnLifecycle() ব্যবহার করা হয়।
  • StateFlow vs LiveData: StateFlow Lifecycle এর উপর নির্ভর করে না, স্পষ্ট সাবস্ক্রিপশন পরিচালনার প্রয়োজন, কিন্তু করুটিন এবং মাল্টিপ্ল্যাটফর্ম সমর্থন করে।
  • stateIn() — SharingStarted কৌশল সহ যেকোনো Flow কে StateFlow এ রূপান্তর করার অপারেটর।

Kotlin এ StateFlow কি?

StateFlow হল kotlinx.coroutines.flow লাইব্রেরির একটি ইন্টারফেস, যা 1 এর নির্দিষ্ট replay প্যারামিটার সহ MutableSharedFlow কে প্রসারিত করে। এর অর্থ হল StateFlow সর্বদা শেষ পাঠানো মান মনে রাখে এবং প্রতিটি নতুন সাবস্ক্রাইবারকে তাৎক্ষণিকভাবে রিপ্লে করে। LiveData এর বিপরীতে, StateFlow স্ট্যান্ডার্ড Kotlin Coroutines লাইব্রেরির অংশ এবং এর Android এ কোন নির্ভরতা নেই।

ধারণাগতভাবে, StateFlow একটি রিঅ্যাকটিভ প্রপার্টি: আপনি .value এর মাধ্যমে এর বর্তমান মান পড়েন এবং .collect() এর মাধ্যমে পরিবর্তনগুলিতে সাবস্ক্রাইব করেন। এই মডেলটিকে "হট ফ্লো" বলা হয় — ডেটা উৎস সাবস্ক্রাইবারদের উপস্থিতি নির্বিশেষে সক্রিয় থাকে, "কোল্ড" ফ্লো এর বিপরীতে যা flow { } এর মাধ্যমে তৈরি হয় এবং সাবস্ক্রাইবার আসার সময় শুরু হয়।

StateFlow kotlinx.coroutines 1.3.7 (ডিসেম্বর 2020) এ স্থিতিশীল হয়েছিল এবং Google I/O 2021 থেকে Google দ্বারা LiveData এর প্রতিস্থাপন হিসাবে সুপারিশ করা হয়েছিল। জানুয়ারী 2025 পর্যন্ত, JetBrains জরিপ অনুসারে, Kotlin এ 56% নতুন Android প্রকল্প StateFlow কে প্রাথমিক রিঅ্যাকটিভ কন্টেইনার হিসাবে ব্যবহার করে।

StateFlow vs LiveData: মূল পার্থক্য

StateFlow এবং LiveData এর মধ্যে পছন্দ প্রকল্পের আর্কিটেকচার, প্রযুক্তি স্ট্যাক এবং প্ল্যাটফর্ম স্বাধীনতার প্রয়োজনীয়তার উপর নির্ভর করে। নীচে ছয়টি মূল মানদণ্ডে তুলনা দেওয়া হল।

মানদণ্ডStateFlowLiveData
প্ল্যাটফর্মKotlin Multiplatform (Android, iOS, সার্ভার)শুধুমাত্র Android
Lifecycle-awareনা — repeatOnLifecycle() প্রয়োজনহ্যাঁ — অন্তর্নির্মিত বাইন্ডিং
করুটিনপূর্ণ সমর্থন (map, filter, combine)liveData { } builder এর মাধ্যমে
Null নিরাপত্তাহ্যাঁ — kotlinx.serialization এর মাধ্যমে সিরিয়ালাইজেবলহ্যাঁ — nullable LiveData<String?> এর মাধ্যমে
কনফ্লেশনকনফ্লেটেড — মধ্যবর্তী মান বাদ দেয়শুধুমাত্র postValue() এর মাধ্যমে
পরীক্ষাrunTest + Turbine বা অন্তর্নির্মিত অপারেটরInstantTaskExecutorRule + observeForever

StateFlow এর View স্তরে স্পষ্ট সাবস্ক্রিপশন পরিচালনার প্রয়োজন: Fragment/Activity এ, সাবস্ক্রিপশন repeatOnLifecycle(STATE.STARTED) { viewModel.uiState.collect { ... } } এর মাধ্যমে করা হয়। এটি LiveData এর স্বয়ংক্রিয় সাবস্ক্রিপশনের চেয়ে বেশি নিয়ন্ত্রণ দেয়, কিন্তু বয়লারপ্লেট কোড যোগ করে। Jetpack Compose এ, সাবস্ক্রিপশন val state by viewModel.uiState.collectAsState() এ সরলীকৃত হয়।

Google সুপারিশ (Android Developers, 2025): নতুন Kotlin প্রকল্পের জন্য StateFlow ব্যবহার করুন, বিশেষ করে Compose এর সাথে কাজ করার সময়। LiveData এর জন্য রাখুন: (1) Java কোড, (2) Java সামঞ্জস্য প্রয়োজন লাইব্রেরি, (3) Room DAO (DAO এর রিটার্ন টাইপ হিসাবে LiveData এখনও জনপ্রিয়)।

MutableStateFlow: প্রকাশনা এবং সাবস্ক্রিপশন

MutableStateFlow হল StateFlow এর একটি পরিবর্তনযোগ্য সংস্করণ যাতে লেখার জন্য উন্মুক্ত value প্রপার্টি রয়েছে। MutableLiveData এর অনুরূপ, MutableStateFlow ViewModel এর ভিতরে ব্যবহৃত হয় এবং বাহ্যিক সাবস্ক্রাইবারদের জন্য StateFlow (শুধুমাত্র পঠনযোগ্য) হিসাবে প্রকাশিত হয়।

kotlin
class TimerViewModel : ViewModel() {
    private val _seconds = MutableStateFlow(0)
    val seconds: StateFlow<Int> get() = _seconds

    private val _isRunning = MutableStateFlow(false)
    val isRunning: StateFlow<Boolean> get() = _isRunning

    private var job: Job? = null

    fun start() {
        if (_isRunning.value) return
        _isRunning.value = true
        job = viewModelScope.launch {
            while (_isRunning.value) {
                delay(1000)
                _seconds.value++
            }
        }
    }

    fun stop() {
        _isRunning.value = false
        job?.cancel()
    }
}

MutableStateFlow এর বৈশিষ্ট্য: (1) মান সর্বদা non-null — কনস্ট্রাক্টরের মাধ্যমে আরম্ভকরণ প্রয়োজন; (2) equals() এর মাধ্যমে পুরানো এবং নতুন মানের তুলনা — যদি নতুন মান পুরানোটির সমান হয়, তাহলে সাবস্ক্রাইবারদের অবহিত করা হয় না; (3) value তে লেখা যেকোনো থ্রেড থেকে সম্ভব, কিন্তু CAS অপারেশনের জন্য কলিং থ্রেডকে শুধুমাত্র সংক্ষিপ্তভাবে ব্লক করে। Kotlin Coroutines ডক্স অনুসারে, equals() এর মাধ্যমে তুলনা LiveData এর তুলনায় অপ্রয়োজনীয় বিজ্ঞপ্তি 90% কমায় — এটি উচ্চ আপডেট ফ্রিকোয়েন্সিতে পারফরম্যান্স লাভ দেয়।

ViewModel এ StateFlow: সেরা অনুশীলন

ViewModel এ StateFlow ব্যবহার করার সময়, এই নিয়মগুলি অনুসরণ করুন: (1) ViewModel এর ভিতরে private মডিফায়ার সহ MutableStateFlow ব্যবহার করুন; (2) get() এর মাধ্যমে শুধুমাত্র পঠনযোগ্য StateFlow প্রকাশ করুন; (3) জটিল স্ক্রিনের জন্য স্টেট টাইপ হিসাবে sealed class ব্যবহার করুন; (4) বর্তমান মানের সমান মান নির্গত করা এড়িয়ে চলুন (StateFlow এটি স্বয়ংক্রিয়ভাবে করে)।

kotlin
// প্রস্তাবিত স্ক্রিন স্টেট স্ট্রাকচার
sealed interface ProfileState {
    data object Loading : ProfileState
    data class Success(
        val name: String,
        val email: String,
        val avatarUrl: String
    ) : ProfileState
    data class Error(val message: String) : ProfileState
}

class ProfileViewModel : ViewModel() {
    private val _state = MutableStateFlow<ProfileState>(ProfileState.Loading)
    val state: StateFlow<ProfileState> get() = _state

    fun loadProfile(userId: String) {
        viewModelScope.launch {
            _state.value = ProfileState.Loading
            try {
                val profile = repository.getProfile(userId)
                _state.value = ProfileState.Success(
                    name = profile.name,
                    email = profile.email,
                    avatarUrl = profile.avatarUrl
                )
            } catch (e: Exception) {
                _state.value = ProfileState.Error(e.message ?: "Unknown error")
            }
        }
    }
}

একক স্টেট টাইপ হিসাবে sealed class ব্যবহার করা Google-সুপারিশকৃত পদ্ধতি (UDF — ইউনিডিরেকশনাল ডেটা ফ্লো)। এটি গ্যারান্টি দেয় যে UI সর্বদা একটি সামঞ্জস্যপূর্ণ অবস্থায় রয়েছে: Loading, Success বা Error, কিন্তু একসাথে নয়। IT Sectr এ, আমরা 2022 সালে সমস্ত স্ক্রিনের জন্য StateFlow + sealed class এ স্যুইচ করেছি — এটি পূর্বাভাসযোগ্য অবস্থার কারণে ViewModel পরীক্ষাকে 40% সরল করেছে।

stateIn() এবং SharingStarted: তিনটি কৌশল

stateIn() একটি অপারেটর যা কোল্ড Flow কে হট StateFlow এ রূপান্তর করে। এটির জন্য CoroutineScope (যেখানে অভ্যন্তরীণ করুটিন চলে) এবং একটি SharingStarted কৌশল নির্দিষ্ট করা প্রয়োজন। SharingStarted এর সঠিক পছন্দ StateFlow এর কর্মক্ষমতা এবং জীবনচক্রকে গুরুত্বপূর্ণভাবে প্রভাবিত করে।

kotlin
// SharingStarted এর তিনটি কৌশল:

// 1. SharingStarted.Eagerly — অবিলম্বে শুরু হয়, কখনও থামে না
val eagerFlow = coldFlow.stateIn(
    scope = viewModelScope,
    started = SharingStarted.Eagerly,
    initialValue = 0
)

// 2. SharingStarted.Lazily — প্রথম সাবস্ক্রাইবারে শুরু হয়, কখনও থামে না
val lazyFlow = coldFlow.stateIn(
    scope = viewModelScope,
    started = SharingStarted.Lazily,
    initialValue = 0
)

// 3. SharingStarted.WhileSubscribed() — সাবস্ক্রাইবার থাকলে শুরু হয়,
//    শেষ সাবস্ক্রাইবার চলে যাওয়ার পর stopTimeoutMillis (ডিফল্ট 0) পরে থামে
val whileSubscribedFlow = coldFlow.stateIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000),
    initialValue = 0
)

WhileSubscribed(5000) — ViewModel এর জন্য সর্বোত্তম কৌশল: শেষ সাবস্ক্রাইবার চলে যাওয়ার পরে, অভ্যন্তরীণ করুটিন আরও 5 সেকেন্ড কাজ করতে থাকে। যদি ব্যবহারকারী এই সময়ের মধ্যে স্ক্রিনে ফিরে আসে, তাহলে ফ্লো পুনরায় শুরু না করেই সাবস্ক্রিপশন পুনরুদ্ধার করা হয়। টাইম-আউট দ্রুত স্ক্রিন সুইচ করার সময় ঘন ঘন পুনরায় শুরু হওয়া প্রতিরোধ করে। Google পরীক্ষা (Android Performance, 2024) অনুসারে, WhileSubscribed 5 সেকেন্ডের টাইম-আউট সহ Eagerly এর তুলনায় CPU খরচ 25% কমায়।

কোড উদাহরণ: Kotlin এ StateFlow

উদাহরণ 1: StateFlow এবং Compose সহ ViewModel

একটি সম্পূর্ণ অনুসন্ধান স্ক্রিন যা অনুসন্ধান কোয়েরি, ফলাফল এবং লোডিং অবস্থা সহ। ViewModel Compose এর সাথে রিঅ্যাকটিভ যোগাযোগের জন্য sealed class UIState এবং StateFlow ব্যবহার করে।

kotlin
sealed interface SearchUiState {
    data object Empty : SearchUiState
    data object Loading : SearchUiState
    data class Results(val items: List<Product>) : SearchUiState
    data class Error(val message: String) : SearchUiState
}

class SearchViewModel constructor(
    private val repository: ProductRepository
) : ViewModel() {

    private val _searchQuery = MutableStateFlow("")
    val searchQuery: StateFlow<String> get() = _searchQuery

    private val _uiState = MutableStateFlow<SearchUiState>(SearchUiState.Empty)
    val uiState: StateFlow<SearchUiState> get() = _uiState

    init {
        viewModelScope.launch {
            _searchQuery
                .debounce(300)
                .filter { it.length >= 3 }
                .flatMapLatest { query ->
                    _uiState.value = SearchUiState.Loading
                    repository.searchProducts(query)
                }
                .collect { products ->
                    _uiState.value = SearchUiState.Results(products)
                }
        }
    }

    fun onQueryChanged(query: String) {
        _searchQuery.value = query
    }
}

// Compose এ:
@Composable
fun SearchScreen(viewModel: SearchViewModel = hiltViewModel()) {
    val uiState by viewModel.uiState.collectAsState()
    // ... Loading, Results, Error অবস্থায় প্রতিক্রিয়া দেখানো UI
}

উদাহরণ 2: Room এবং combine সহ StateFlow

Room (সংস্করণ 2.4.0 থেকে) DAO থেকে Flow ফেরত সমর্থন করে। combine এর মাধ্যমে একাধিক Flow একত্রিত করা জটিল স্ক্রিনের জন্য একটি শক্তিশালী প্যাটার্ন।

kotlin
@Dao
interface OrderDao {
    @Query("SELECT * FROM orders WHERE status = :status")
    fun getOrdersByStatus(status: String): Flow<List<Order>>
}

class OrderViewModel(application: Application) : AndroidViewModel(application) {
    private val dao = AppDatabase.getDatabase(application).orderDao()

    val activeOrders: StateFlow<List<Order>> = dao.getOrdersByStatus("active")
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())

    val summary: StateFlow<OrderSummary> = combine(
        dao.getOrdersByStatus("active"),
        dao.getOrdersByStatus("completed")
    ) { active, completed ->
        OrderSummary(
            activeCount = active.size,
            completedCount = completed.size,
            totalAmount = (active + completed).sumOf { it.amount }
        )
    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), OrderSummary(0, 0, 0.0))
}

Room স্বয়ংক্রিয়ভাবে orders টেবিলের পরিবর্তনগুলি ট্র্যাক করে এবং যেকোনো পরিবর্তনে ডেটা পুনরায় কোয়েরি করে। StateFlow + Room হল Room + LiveData এর আধুনিক প্রতিস্থাপন। Google (Android Architecture Guide, 2025) অনুসারে, Flow + StateFlow + Room স্ট্যাক সমস্ত Kotlin প্রকল্পের জন্য সুপারিশ করা হয় যেখানে ডাটাবেস পরিবর্তনে রিঅ্যাকটিভ UI আপডেট প্রয়োজন।

সচরাচর জিজ্ঞাস্য

StateFlow এ কনফ্লেশন (conflation) কী?

কনফ্লেশন একটি প্রক্রিয়া যেখানে StateFlow শুধুমাত্র শেষ পাঠানো মান রাখে। যদি সাবস্ক্রাইবার পূর্ববর্তী মান প্রক্রিয়াকরণের আগে একটি নতুন মান পাঠানো হয়, তাহলে মধ্যবর্তী মান হারিয়ে যায়। এটি UI এর জন্য গুরুত্বপূর্ণ: যদি অবস্থা Loading → Success → Error এ পরিবর্তিত হয়, এবং UI Success রেন্ডার না করে, তাহলে এটি অতিরিক্ত রেন্ডারিং ছাড়াই সরাসরি Error এ চলে যায়। কনফ্লেশন একটি মূল Android অপ্টিমাইজেশন যা Compose এ অত্যধিক রিকম্পোজিশন প্রতিরোধ করে।

LiveData কে StateFlow এ কীভাবে রূপান্তর করবেন?

lifecycle-livedata-ktx লাইব্রেরি থেকে এক্সটেনশন ফাংশন liveData.asFlow() ব্যবহার করুন, তারপর StateFlow এ রূপান্তর করতে .stateIn() ব্যবহার করুন। বিপরীত রূপান্তর হল stateFlow.asLiveData()। LiveData থেকে StateFlow এ মাইগ্রেট করার সময় রূপান্তর কার্যকর: আপনি ধীরে ধীরে ViewModels কে StateFlow এ রূপান্তর করতে পারেন যখন পুরানো View LiveData এর মাধ্যমে সাবস্ক্রাইব থাকে।

কেন StateFlow এর একটি প্রাথমিক মান প্রয়োজন?

StateFlow এর সর্বদা একটি মান থাকা আবশ্যক — এটি ইন্টারফেস চুক্তি: যে কোনো নতুন সংযুক্ত সাবস্ক্রাইবার অপেক্ষা না করেই তাৎক্ষণিকভাবে বর্তমান অবস্থা পায়। প্রাথমিক মান MutableStateFlow(initialValue) কনস্ট্রাক্টর বা stateIn(initialValue) অপারেটরে পাঠানো হয়। যদি অবস্থা অনুপস্থিত থাকতে পারে, তাহলে nullable টাইপ সহ MutableStateFlow<T?>(null) ব্যবহার করুন এবং UI তে null হ্যান্ডেল করুন।

StateFlow কি থ্রেড-নিরাপদ?

হ্যাঁ, StateFlow থ্রেড-নিরাপদ: value পড়া এবং লেখা পারমাণবিক অপারেশন (CAS) ব্যবহার করে। তবে, collect() একটি suspend ফাংশন এবং এটি একটি করুটিনে লঞ্চ করা আবশ্যক। যদি নির্গমন এবং সংগ্রহ ভিন্ন থ্রেডে ঘটে, StateFlow value তে সমস্ত অপারেশনের জন্য happens-before গ্যারান্টি দেয়। View এ StateFlow সংগ্রহ করতে, lifecycleScope.launch { repeatOnLifecycle(STATE.STARTED) { stateFlow.collect { ... } } } ব্যবহার করুন।

একটি ViewModel এ কতগুলি StateFlow রাখা যায়?

কোনো কঠোর সীমা নেই, তবে প্রতি স্ক্রিনে 3-5 টির বেশি পৃথক StateFlow না ব্যবহার করার সুপারিশ করা হয়। যদি আরও ভিন্ন অবস্থার প্রয়োজন হয়, তবে সেগুলিকে sealed class বা data class এর মাধ্যমে একটিতে একত্রিত করুন। প্রতিটি StateFlow সংগ্রহের সময় একটি Continuation অবজেক্ট বরাদ্দ প্রয়োজন — একশটি StateFlow GC তে লক্ষণীয় চাপ তৈরি করতে পারে। Google এর সুপারিশ অনুসারে, প্রতি স্ক্রিনে একটি sealed class UIState পড়ার যোগ্যতা এবং কর্মক্ষমতার মধ্যে সর্বোত্তম ভারসাম্য।

সারসংক্ষেপ

  • StateFlow — Kotlin Coroutines থেকে একটি হট রিঅ্যাকটিভ কন্টেইনার (replay=1), সর্বদা শেষ মান ধারণ করে।
  • StateFlow vs LiveData: StateFlow Lifecycle থেকে স্বাধীন, করুটিন এবং মাল্টিপ্ল্যাটফর্ম সমর্থন করে; LiveData এর স্বয়ংক্রিয় সাবস্ক্রিপশন রয়েছে।
  • MutableStateFlow private set সহ এবং শুধুমাত্র পঠনযোগ্য StateFlow প্রকাশ করা — ViewModel এর জন্য স্ট্যান্ডার্ড প্যাটার্ন।
  • UIState হিসাবে Sealed class — জটিল স্ক্রিন অবস্থা পরিচালনার জন্য Google-সুপারিশকৃত UDF পদ্ধতি।
  • stateIn() WhileSubscribed(5000) সহ — ViewModel এর জন্য কোল্ড Flow কে StateFlow এ রূপান্তরের সর্বোত্তম কৌশল।
  • Room DAO থেকে Flow ফেরত দেয় — StateFlow + combine + Room, Room + LiveData প্রতিস্থাপন করে।
  • Google নতুন Kotlin প্রকল্পের জন্য StateFlow সুপারিশ করে, বিশেষ করে Jetpack Compose এর সাথে।

আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব

IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।

প্রকল্প নিয়ে আলোচনা করুন

আরও পড়ুন