網(wǎng)站首頁 編程語言 正文
一、Android分層架構(gòu)
不管是早期的MVC、MVP,還是最新的MVVM和MVI架構(gòu),這些框架一直解決的都是一個(gè)數(shù)據(jù)流的問題。一個(gè)良好的數(shù)據(jù)流框架,每一層的職責(zé)是單一的。例如,我們可以在表現(xiàn)層(Presentation Layer)的基礎(chǔ)上添加一個(gè)領(lǐng)域?qū)樱―omain Layer) 來保存業(yè)務(wù)邏輯,使用數(shù)據(jù)層(Data Layer)對(duì)上層屏蔽數(shù)據(jù)來源(數(shù)據(jù)可能來自遠(yuǎn)程服務(wù),可能是本地?cái)?shù)據(jù)庫)。
在Android中,一個(gè)典型的Android分層架構(gòu)圖如下:
其中,我們需要重點(diǎn)看下Presenter 和 ViewModel, Presenter 和 ViewModel向 View 提供數(shù)據(jù)的機(jī)制是不同的。
- Presenter: Presenter通過持有 View 的引用并直接調(diào)用操作 View,以此向 View 提供和更新數(shù)據(jù)。
- ViewModel:ViewModel 通過將可觀察的數(shù)據(jù)暴露給觀察者來向 View 提供和更新數(shù)據(jù)。
目前,官方提供的可觀察的數(shù)據(jù)組件有LiveData、StateFlow和SharedFlow??赡艽蠹覍?duì)LiveData比較熟悉,配合ViewModel可以很方便的實(shí)現(xiàn)數(shù)據(jù)流的流轉(zhuǎn)。不過,LiveData也有很多常見的缺陷,并且使用場(chǎng)景也比較固定,如果網(wǎng)上出現(xiàn)了KotlinFlow 替代 LiveData的聲音。那么 Flow 真的會(huì)替代 LiveData嗎?Flow 真的適合你的項(xiàng)目嗎?看完下面的分析后,你定會(huì)有所收獲。
二、ViewModel + LiveData
ViewModel的作用是將視圖和邏輯進(jìn)行分離,Activity或者Fragment只負(fù)責(zé)UI顯示部分,網(wǎng)絡(luò)請(qǐng)求或者數(shù)據(jù)庫操作則有ViewModel負(fù)責(zé)。ViewModel旨在以注重生命周期的方式存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù),讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)留存。并且ViewModel不持有View層的實(shí)例,通過LiveData與Activity或者Fragment通訊,不需要擔(dān)心潛在的內(nèi)存泄漏問題。
而LiveData 則是一種可觀察的數(shù)據(jù)存儲(chǔ)器類,與常規(guī)的可觀察類不同,LiveData 具有生命周期感知能力,它遵循其他應(yīng)用組件(如 Activity、Fragment 或 Service)的生命周期。這種感知能力可確保LiveData當(dāng)數(shù)據(jù)源發(fā)生變化的時(shí)候,通知它的觀察者更新UI界面。同時(shí)它只會(huì)通知處于Active狀態(tài)的觀察者更新界面,如果某個(gè)觀察者的狀態(tài)處于Paused或Destroyed時(shí)那么它將不會(huì)收到通知,所以不用擔(dān)心內(nèi)存泄漏問題。
下面是官方發(fā)布的架構(gòu)組件庫的生命周期的說明:
2.1 LiveData 特性
通過前面的介紹可以知道,LiveData 是 Android Jetpack Lifecycle 組件中的內(nèi)容,具有生命周期感知能力。一句話概括就是:LiveData 是可感知生命周期的,可觀察的,數(shù)據(jù)持有者。
特點(diǎn)如下:
- 觀察者的回調(diào)永遠(yuǎn)發(fā)生在主線程
- 僅持有單個(gè)且最新的數(shù)據(jù)
- 自動(dòng)取消訂閱
- 提供「可讀可寫」和「僅可讀」兩個(gè)版本收縮權(quán)限
- 配合 DataBinding 實(shí)現(xiàn)「雙向綁定」
觀察者的回調(diào)永遠(yuǎn)發(fā)生在主線程
因?yàn)長(zhǎng)iveData 是被用來更新 UI的,因此 Observer 接口的 onChanged() 方法必須在主線程回調(diào)。
public interface Observer<T> {
void onChanged(T t);
}
背后的道理也很簡(jiǎn)單,LiveData 的 setValue() 發(fā)生在主線程(非主線程調(diào)用會(huì)拋異常),而如果調(diào)用postValue()方法,則它的內(nèi)部會(huì)切換到主線程調(diào)用 setValue()。
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
可以看到,postValue()方法的內(nèi)部調(diào)用了postToMainThread()實(shí)現(xiàn)線程的切換,之后遍歷所有觀察者的 onChanged() 方法。
僅持有單個(gè)且最新數(shù)據(jù)
作為數(shù)據(jù)持有者,LiveData僅持有【單個(gè)且最新】的數(shù)據(jù)。單個(gè)且最新,意味著 LiveData 每次只能持有一個(gè)數(shù)據(jù),如果有新數(shù)據(jù)則會(huì)覆蓋上一個(gè)。并且,由于LiveData具備生命周期感知能力,所以觀察者只會(huì)在活躍狀態(tài)下(STARTED 到 RESUMED)才會(huì)接收到 LiveData 最新的數(shù)據(jù),在非活躍狀態(tài)下則不會(huì)收到。
自動(dòng)取消訂閱
可感知生命周期的重要優(yōu)勢(shì)就是可以自動(dòng)取消訂閱,這意味著開發(fā)者無需手動(dòng)編寫那些取消訂閱的模板代碼,降低了內(nèi)存泄漏的可能性。背后的實(shí)現(xiàn)邏輯是在生命周期處于 DESTROYED 時(shí),移除觀察者。
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
... //省略其他代碼
}
提供「可讀可寫」和「僅可讀」兩種方式
LiveData 提供了setValue() 和 postValue()兩種方式來操作實(shí)體數(shù)據(jù),而為了細(xì)化權(quán)限,LiveData又提供了mutable(MutableLiveData) 和 immutable(LiveData) 兩個(gè)類,前者「可讀可寫」,后者則「僅可讀」。
配合 DataBinding 實(shí)現(xiàn)「雙向綁定」
LiveData 配合 DataBinding 可以實(shí)現(xiàn)更新數(shù)據(jù)自動(dòng)驅(qū)動(dòng)UI變化,如果使用「雙向綁定」還能實(shí)現(xiàn) UI 變化影響數(shù)據(jù)的變化功能。
2.2 LiveData的缺陷
正如前面說的,LiveData有自己的使用場(chǎng)景,只有滿足使用場(chǎng)景才會(huì)最大限度的發(fā)揮它的功能,而下面這些則是在設(shè)計(jì)時(shí)將自帶的一些缺陷:
- value 可以是 nullable 的
- 在 fragment 訂閱時(shí)需要傳入正確的 lifecycleOwner
- 當(dāng) LiveData 持有的數(shù)據(jù)是「事件」時(shí),可能會(huì)遇到「粘性事件」
- LiveData 是不防抖的
- LiveData 的 transformation 需要工作在主線程
value 可以是 nullable 的
由于LiveData的getValue() 是可空的,所以在使用時(shí)應(yīng)該注意判空,否則容易出現(xiàn)空指針的報(bào)錯(cuò)。
@Nullable
public T getValue() {
Object data = mData;
if (data != NOT_SET) {
return (T) data;
}
return null;
}
傳入正確的 lifecycleOwner
Fragment 調(diào)用 LiveData的observe() 方法時(shí)傳入 this 和 viewLifecycleOwner 的含義是不一樣的。因?yàn)镕ragment與Fragment中的View的生命周期并不一致,有時(shí)候我們需要的讓observer感知Fragment中的View的生命周期而非Fragment。
粘性事件
粘性事件的定義是,發(fā)射的事件如果早于注冊(cè),那么注冊(cè)之后依然可以接收到的事件,這一現(xiàn)象稱為粘性事件。解決辦法是:將事件作為狀態(tài)的一部分,在事件被消費(fèi)后,不再通知觀察者。推薦兩種解決方式:
- KunMinX/UnPeek-LiveData
- 使用kotlin 擴(kuò)展函數(shù)和 typealias 封裝解決「粘性」事件的 LiveData
默認(rèn)不防抖
當(dāng)setValue()/postValue() 傳入相同的值且多次調(diào)用時(shí),觀察者的 onChanged() 也會(huì)被多次調(diào)用。不過,嚴(yán)格來講,這也不算一個(gè)問題,我們只需要在調(diào)用 setValue()/postValue() 前判斷一下 vlaue 與之前是否相同即可。
transformation 工作在主線程
有些時(shí)候,我們需要對(duì)從Repository 層得到的數(shù)據(jù)進(jìn)行處理。例如,從數(shù)據(jù)庫獲得 User列表,我們需要根據(jù) id 獲取某個(gè) User, 那么就需要用到MediatorLiveData 和 Transformatoins 來實(shí)現(xiàn)。
- Transformations.map
- Transformations.switchMap
并且,map 和 switchMap 內(nèi)部均是使用 MediatorLiveData的addSource() 方法實(shí)現(xiàn)的,而該方法會(huì)在主線程調(diào)用,使用不當(dāng)會(huì)有性能問題。
@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
Source<S> e = new Source<>(source, onChanged);
Source<?> existing = mSources.putIfAbsent(source, e);
if (existing != null && existing.mObserver != onChanged) {
throw new IllegalArgumentException(
"This source was already added with the different observer");
}
if (existing != null) {
return;
}
if (hasActiveObservers()) {
e.plug();
}
}
2.3 LiveData 小結(jié)
LiveData 是一種可觀察的數(shù)據(jù)存儲(chǔ)器類,與常規(guī)的可觀察類不同,LiveData 具有生命周期感知能力,它遵循其他應(yīng)用組件(如 Activity、Fragment 或 Service)的生命周期。這種感知能力可確保LiveData當(dāng)數(shù)據(jù)源發(fā)生變化的時(shí)候,通知它的觀察者更新UI界面。同時(shí)它只會(huì)通知處于Active狀態(tài)的觀察者更新界面,如果某個(gè)觀察者的狀態(tài)處于Paused或Destroyed時(shí)那么它將不會(huì)收到通知,所以不用擔(dān)心內(nèi)存泄漏問題。
同時(shí),LiveData 專注單一功能,因此它的一些方法使用上是有局限性的,并且需要配合 ViewModel 使用才能顯示其價(jià)值。
三、Flow
3.1 簡(jiǎn)介
Flow是Google官方提供的一套基于kotlin協(xié)程的響應(yīng)式編程模型,它與RxJava的使用類似,但相比之下Flow使用起來更簡(jiǎn)單,另外Flow作用在協(xié)程內(nèi),可以與協(xié)程的生命周期綁定,當(dāng)協(xié)程取消時(shí),F(xiàn)low也會(huì)被取消,避免了內(nèi)存泄漏風(fēng)險(xiǎn)。
協(xié)程是輕量級(jí)的線程,本質(zhì)上協(xié)程、線程都是服務(wù)于并發(fā)場(chǎng)景下,其中協(xié)程是協(xié)作式任務(wù),線程是搶占式任務(wù)。默認(rèn)協(xié)程用來處理實(shí)時(shí)性不高的數(shù)據(jù),請(qǐng)求到結(jié)果后整個(gè)協(xié)程就結(jié)束了。比如,有下面一個(gè)例子:
其中,紅框中需要展示的內(nèi)容實(shí)時(shí)性不高,而需要交互的,比如轉(zhuǎn)發(fā)和點(diǎn)贊屬于實(shí)時(shí)性很高的數(shù)據(jù)需要定時(shí)刷新。對(duì)于實(shí)時(shí)性不高的場(chǎng)景,直接使用 Kotlin 的協(xié)程處理即可,比如。
suspend fun loadData(): Data
uiScope.launch {
val data = loadData()
updateUI(data)
}
而對(duì)于實(shí)時(shí)性要求較高的場(chǎng)景,上面的方式就不起作用了,此時(shí)需要用到Kotlin提供的Flow數(shù)據(jù)流。
fun dataStream(): Flow<Data>uiScope.launch {
dataStream().collect { data ->
updateUI(data)
}
}
3.2 基本概念
Kotlin的數(shù)據(jù)流主要由三個(gè)成員組成,分別是生產(chǎn)者、消費(fèi)者和中介。 生產(chǎn)者:生成添加到數(shù)據(jù)流中的數(shù)據(jù),可以配合得協(xié)程使用,使用異步方式生成數(shù)據(jù)。 中介(可選):可以修改發(fā)送到數(shù)據(jù)流的值,或修正數(shù)據(jù)流本身。 消費(fèi)者:使用方則使用數(shù)據(jù)流中的值。
其中,中介可以對(duì)數(shù)據(jù)流中的數(shù)據(jù)進(jìn)行更改,甚至可以更改數(shù)據(jù)流本身,他們的架構(gòu)示意圖如下。
在Kotlin中,F(xiàn)low 是一種冷流,不過有一種特殊的Flow( StateFlow/SharedFlow) 是熱流。什么是冷流,他和熱流又有什么關(guān)系呢?
冷流:只有訂閱者訂閱時(shí),才開始執(zhí)行發(fā)射數(shù)據(jù)流的代碼。并且冷流和訂閱者只能是一對(duì)一的關(guān)系,當(dāng)有多個(gè)不同的訂閱者時(shí),消息是重新完整發(fā)送的。也就是說對(duì)冷流而言,有多個(gè)訂閱者的時(shí)候,他們各自的事件是獨(dú)立的。 熱流:無論有沒有訂閱者訂閱,事件始終都會(huì)發(fā)生。當(dāng) 熱流有多個(gè)訂閱者時(shí),熱流與訂閱者們的關(guān)系是一對(duì)多的關(guān)系,可以與多個(gè)訂閱者共享信息。
3.3 StateFlow
前面說過,冷流和訂閱者只能是一對(duì)一的關(guān)系,當(dāng)我們要實(shí)現(xiàn)一個(gè)流多個(gè)訂閱者的場(chǎng)景時(shí),就需要使用熱流了。
StateFlow 是一個(gè)狀態(tài)容器式可觀察數(shù)據(jù)流,可以向其收集器發(fā)出當(dāng)前狀態(tài)更新和新狀態(tài)更新。可以通過其 value 屬性讀取當(dāng)前狀態(tài)值,如需更新狀態(tài)并將其發(fā)送到數(shù)據(jù)流,那么就需要使用MutableStateFlow。
基本使用
在Android 中,StateFlow 非常適合需要讓可變狀態(tài)保持可觀察的類。由于StateFlow并不是系統(tǒng)API,所以使用前需要添加依賴:
dependencies {
... //省略其他
implementation "androidx.activity:activity-ktx:1.3.1"
implementation "androidx.fragment:fragment-ktx:1.4.1"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'
}
接著,我們需要?jiǎng)?chuàng)建一個(gè)ViewModel,比如:
class StateFlowViewModel: ViewModel() {
val data = MutableStateFlow<Int>(0)
fun add(v: View) {
data.value++
}
fun del(v: View) {
data.value--
}
}
可以看到,我們使用MutableStateFlow包裹需要操作的數(shù)據(jù),并添加了add()和del()兩個(gè)方法。然后,我們?cè)倬帉懸欢螠y(cè)試代碼實(shí)現(xiàn)數(shù)據(jù)的修改,并自動(dòng)刷新數(shù)據(jù)。
class StateFlowActivity : AppCompatActivity() {
private val viewModel by viewModels<StateFlowViewModel>()
private val mBinding : ActivityStateFlowBinding by lazy {
ActivityStateFlowBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
initFlow()
}
private fun initFlow() {
mBinding.apply {
btnAdd.setOnClickListener {
viewModel.add(it)
}
btnDel.setOnClickListener {
viewModel.del(it)
}
}
}
}
上面代碼中涉及到的布局代碼如下:
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="stateFlowViewModel" type="com.xzh.demo.flow.StateFlowViewModel" /> </data> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="200dp" android:layout_marginTop="30dp" android:text="@{String.valueOf(stateFlowViewModel.data)}" android:textSize="24sp" /> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/btn_add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|start" android:layout_marginStart="10dp" android:layout_marginBottom="10dp" android:contentDescription="start" android:src="@android:drawable/ic_input_add" /> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/btn_del" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginEnd="10dp" android:layout_marginBottom="10dp" android:contentDescription="cancel" android:src="@android:drawable/ic_menu_close_clear_cancel" /> </FrameLayout> </layout>
上面代碼中,我們使用了DataBing寫法,因此不需要再手動(dòng)的綁定數(shù)據(jù)和刷新數(shù)據(jù)。
3.4 SharedFlow
SharedFlow基本概念
SharedFlow提供了SharedFlow 與 MutableSharedFlow兩個(gè)版本,平時(shí)使用較多的是MutableSharedFlow。它們的區(qū)別是,SharedFlow可以保留歷史數(shù)據(jù),MutableSharedFlow 沒有起始值,發(fā)送數(shù)據(jù)時(shí)需要調(diào)用 emit()/tryEmit() 方法。
首先,我們來看看SharedFlow的構(gòu)造函數(shù):
public fun <T> MutableSharedFlow(
replay: Int = 0,
extraBufferCapacity: Int = 0,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
): MutableSharedFlow<T>
可以看到,MutableSharedFlow需要三個(gè)參數(shù):
- replay:表示當(dāng)新的訂閱者Collect時(shí),發(fā)送幾個(gè)已經(jīng)發(fā)送過的數(shù)據(jù)給它,默認(rèn)為0,即默認(rèn)新訂閱者不會(huì)獲取以前的數(shù)據(jù)
- extraBufferCapacity:表示減去replay,MutableSharedFlow還緩存多少數(shù)據(jù),默認(rèn)為0
- onBufferOverflow:表示緩存策略,即緩沖區(qū)滿了之后Flow如何處理,默認(rèn)為掛起。除此之外,還支持DROP_OLDEST 和DROP_LATEST 。
//ViewModel
val sharedFlow=MutableSharedFlow<String>()
viewModelScope.launch{
sharedFlow.emit("Hello")
sharedFlow.emit("SharedFlow")
}
//Activity
lifecycleScope.launch{
viewMode.sharedFlow.collect {
print(it)
}
}
基本使用
SharedFlow并不是系統(tǒng)API,所以使用前需要添加依賴:
dependencies {
... //省略其他
implementation "androidx.activity:activity-ktx:1.3.1"
implementation "androidx.fragment:fragment-ktx:1.4.1"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'
}
接下來,我們創(chuàng)建一個(gè)SharedFlow,由于需要一對(duì)多的進(jìn)行通知,所以我們MutableSharedFlow,然后重寫postEvent()方法,
代碼如下:
object LocalEventBus {
private val events= MutableSharedFlow< Event>()
suspend fun postEvent(event: Event){
events.emit(event)
}
}
data class Event(val timestamp:Long)
接下來,我們?cè)賱?chuàng)建一個(gè)ViewModel,里面添加startRefresh()和cancelRefresh()兩個(gè)方法,
如下:
class SharedViewModel: ViewModel() {
private lateinit var job: Job
fun startRefresh(){
job=viewModelScope.launch (Dispatchers.IO){
while (true){
LocalEventBus.postEvent(Event(System.currentTimeMillis()))
}
}
}
fun cancelRefresh(){
job.cancel()
}
}
前面說過,一個(gè)典型的Flow是由三部分構(gòu)成的。所以,此處我們先新建一個(gè)用于數(shù)據(jù)消費(fèi)的Fragment
代碼如下:
class FlowFragment: Fragment() {
private val mBinding : FragmentFlowBinding by lazy {
FragmentFlowBinding.inflate(layoutInflater)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return mBinding.root
}
override fun onStart() {
super.onStart()
lifecycleScope.launchWhenCreated {
LocalEventBus.events.collect {
mBinding.tvShow.text=" ${it.timestamp}"
}
}
}
}
FlowFragment的主要作用就是接收LocalEventBus的數(shù)據(jù),并顯示到視圖上。接下來,我們還需要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)的生產(chǎn)者,為了簡(jiǎn)單,我們只在生產(chǎn)者頁面中開啟協(xié)程,
代碼如下:
class FlowActivity : AppCompatActivity() {
private val viewModel by viewModels<SharedViewModel>()
private val mBinding : ActivityFlowBinding by lazy {
ActivityFlowBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
initFlow()
}
private fun initFlow() {
mBinding.apply {
btnStart.setOnClickListener {
viewModel.startRefresh()
}
btnStop.setOnClickListener {
viewModel.cancelRefresh()
}
}
}
}
其中,F(xiàn)lowActivity代碼中涉及的布局如下:
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> </data> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".fragment.SharedFragment"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <fragment android:name="com.xzh.demo.FlowFragment" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> </LinearLayout> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/btn_start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|start" android:layout_marginStart="10dp" android:layout_marginBottom="10dp" android:src="@android:drawable/ic_input_add" android:contentDescription="start" /> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/btn_stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginEnd="10dp" android:layout_marginBottom="10dp" android:src="@android:drawable/ic_menu_close_clear_cancel" android:contentDescription="cancel" /> </FrameLayout> </layout>
最后,當(dāng)我們運(yùn)行上面的代碼時(shí),就會(huì)在FlowFragment的頁面上顯示當(dāng)前的時(shí)間戳,并且頁面的數(shù)據(jù)會(huì)自動(dòng)進(jìn)行刷新。
3.5 冷流轉(zhuǎn)熱流
前文說過,Kotlin的Flow是一種冷流,而StateFlow/SharedFlow則屬于熱流。那么有人會(huì)問:怎么將冷流轉(zhuǎn)化為熱流呢?答案就是kotlin提供的shareIn()和stateIn()兩個(gè)方法。
首先,來看一下StateFlow的shareIn的定義:
public fun <T> Flow<T>.stateIn(
scope: CoroutineScope,
started: SharingStarted,
initialValue: T
): StateFlow<T>
shareIn方法將流轉(zhuǎn)換為SharedFlow,需要三個(gè)參數(shù),我們重點(diǎn)看一下started參數(shù),表示流啟動(dòng)的條件,支持三種:
- SharingStarted.Eagerly:無論當(dāng)前有沒有訂閱者,流都會(huì)啟動(dòng),訂閱者只能接收到replay個(gè)緩沖區(qū)的值。
- SharingStarted.Lazily:當(dāng)有第一個(gè)訂閱者時(shí),流才會(huì)開始,后面的訂閱者只能接收到replay個(gè)緩沖區(qū)的值,當(dāng)沒有訂閱者時(shí)流還是活躍的。
- SharingStarted.WhileSubscribed:只有滿足特定的條件時(shí)才會(huì)啟動(dòng)。
接下來,我們?cè)诳匆幌耂haredFlow的shareIn的定義:
public fun <T> Flow<T>.shareIn(
scope: CoroutineScope,
started: SharingStarted,
replay: Int = 0
): SharedFlow<T>
此處,我們重點(diǎn)看下replay參數(shù),該參數(shù)表示轉(zhuǎn)換為SharedFlow之后,當(dāng)有新的訂閱者的時(shí)候發(fā)送緩存中值的個(gè)數(shù)。
3.6 StateFlow與SharedFlow對(duì)比
從前文的介紹可以知道,StateFlow與SharedFlow都是熱流,都是為了滿足流的多個(gè)訂閱者的使用場(chǎng)景的,一時(shí)間讓人有些傻傻分不清,那StateFlow與SharedFlow究竟有什么區(qū)別呢?總結(jié)起來,大概有以下幾點(diǎn):
- SharedFlow配置更為靈活,支持配置replay、緩沖區(qū)大小等,StateFlow是SharedFlow的特殊化版本,replay固定為1,緩沖區(qū)大小默認(rèn)為0。
- StateFlow與LiveData類似,支持通過myFlow.value獲取當(dāng)前狀態(tài),如果有這個(gè)需求,必須使用StateFlow。
- SharedFlow支持發(fā)出和收集重復(fù)值,而StateFlow當(dāng)value重復(fù)時(shí),不會(huì)回調(diào)collect給新的訂閱者,StateFlow只會(huì)重播當(dāng)前最新值,SharedFlow可配置重播元素個(gè)數(shù)(默認(rèn)為0,即不重播)。
從上面的描述可以看出,StateFlow為我們做了一些默認(rèn)的配置,而SharedFlow澤添加了一些默認(rèn)約束??偟膩碚f,SharedFlow相比StateFlow更靈活。
四、總結(jié)
目前,官方提供的可觀察的數(shù)據(jù)組件有LiveData、StateFlow和SharedFlow。LiveData是Android早期的數(shù)據(jù)流組件,具有生命周期感知能力,需要配合ViewModel才能實(shí)現(xiàn)它的價(jià)值。不過,LiveData也有很多使用場(chǎng)景缺陷,常見的有粘性事件、不支持防抖等。
于是,Kotlin在1.4.0版本,陸續(xù)推出了StateFlow與SharedFlow兩個(gè)組件,StateFlow與SharedFlow都是熱流,都是為了滿足流的多個(gè)訂閱者的使用場(chǎng)景,不過它們也有微妙的區(qū)別,具體參考前面內(nèi)容的說明。
原文鏈接:https://juejin.cn/post/7116704131141615629
相關(guān)推薦
- 2022-12-06 C#基礎(chǔ)教程之類class與結(jié)構(gòu)struct的區(qū)別_C#教程
- 2022-05-11 linq中的分組操作符_實(shí)用技巧
- 2022-05-20 C++實(shí)現(xiàn)公司人事管理系統(tǒng)_C 語言
- 2023-05-07 C語言中大小端問題實(shí)例探索解決方法_C 語言
- 2023-06-16 GO語言中Chan實(shí)現(xiàn)原理的示例詳解_Golang
- 2022-04-25 C語言的結(jié)構(gòu)體你了解嗎_C 語言
- 2022-10-30 C++解析obj模型文件方法介紹_C 語言
- 2022-08-02 Android開發(fā)雙向滑動(dòng)選擇器范圍SeekBar實(shí)現(xiàn)_Android
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支