<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在 Jetpack 架構規範中, ViewModel 與 View 之間應該遵循單向資料流的通訊方式,Events
永遠從 View 流向 VM ,而 State
從 VM 流向 View。
如果 ViewModel 對 View 暴露了不適當的介面型別,則會破壞單向資料流的形成。不適當的介面型別常見於以下兩點:
ViewModel 對外暴露的資料狀態,無論是 LiveData 或是 StateFlow 都應該使用 Immutable 的介面型別進行暴露而非 Mutable 的具體實現。View 只能單向訂閱這些狀態的變化,避免對狀態反向更新。
class MyViewModel: ViewModel() { private val _loading = MutableLiveData<Boolean>() val loading: LiveData<Boolean> get() = _loading }
未來避免暴露 Mutable 型別,我們需要像上面這樣處理,將 loading
的具體實現定義為一個 private
的 Mutable 型別,便於內部更新。
private val _loading : MutableStateFlow<Boolean?> = MutableStateFlow(null) val loading = _loading.asStateFlow()
StateFlow 的寫法也類似,但是通過 asStateFlow
可以少寫一個型別宣告,但是要注意此時不要使用 custom get(), 不然 asStateFlow
會執行多次。
每次都要多宣告一個帶劃線的私有變數會讓程式碼顯得有些累贅,也正因如此,有 issue 希望 Kotlin 增加類似下面的語法使得對外對內可以暴露不同型別。
//https://youtrack.jetbrains.com/issue/KT-14663 private val loading = MutableLiveData<Boolean>() public get(): LiveData<Boolean>
在新語法還未出現的當下,一個讓程式碼變整潔的思路是為 ViewModel 提取對外暴露的抽象類:
abstract class MyViewModel: ViewModel() { abstract val loading: LiveData<Boolean> } class MyViewModelImpl: MyViewModel() { override val loading = MutableLiveData<Boolean>() fun doSomeWork() { // ... loading.value = true } }
如上, MyViewModelImpl
內重寫的 loading
可以作為 Mutable 型別使用。雖然這種做法會增加了一個抽象類程式碼量不減反增,但是它使 MyViewModelImpl
內的程式碼更加簡潔,而且對外可以隱藏更多 ViewModel 的實現細節,封裝性更好。
但是需要特別注意的是,為了建立 MyViewModel
必須使用自定義 Factory:
val vm : MyViewModel by viewModels { MyViewModelFactory() }
如果你的工程引入了 Hilt ,那麼可以通過 @Bind
繫結 ViewModel 的介面與實現,無需自定義 Factory 了,寫法跟以前一樣,直接使用 by viewModels()
即可
@Module @InstallIn(ViewModelComponent::class) abstract class MyViewModule { @Binds abstract fun MyViewModel(instance: MyViewModelImpl): MyViewModel } @HiltViewModel class MyViewModelImpl @Inject constructor() : MyViewModel()
相對於暴露 Mutable 狀態,暴露 Suspend 方法的錯誤則更為常見。
按照單向資料流的思想 ViewModel 需要提供 API 給 View 用於傳送 Events,我們在定義 API 時需要注意避免使用 Suspend 函數,理由如下:
ViewModelScope
可以保證一些耗時任務的穩定執行。如果暴露掛起函數給 View,則協程需要在 lifecycleScope
中啟動,在橫豎屏等場景中會中斷任務的進行。因此,ViewModel 為 View 暴露的 API 應該是非掛起且無法返回值的方法,以下是官網的程式碼範例:
// DO create coroutines in the ViewModel class LatestNewsViewModel( private val getLatestNewsWithAuthors: GetLatestNewsWithAuthorsUseCase ) : ViewModel() { private val _uiState = MutableStateFlow<LatestNewsUiState>(LatestNewsUiState.Loading) val uiState: StateFlow<LatestNewsUiState> = _uiState fun loadNews() { viewModelScope.launch { val latestNewsWithAuthors = getLatestNewsWithAuthors() _uiState.value = LatestNewsUiState.Success(latestNewsWithAuthors) } } } // Prefer observable state rather than suspend functions from the ViewModel class LatestNewsViewModel( private val getLatestNewsWithAuthors: GetLatestNewsWithAuthorsUseCase ) : ViewModel() { // DO NOT do this. News would probably need to be refreshed as well. // Instead of exposing a single value with a suspend function, news should // be exposed using a stream of data as in the code snippet above. suspend fun loadNews() = getLatestNewsWithAuthors() }
程式碼中建議暴露一個普通的無返回值的 loadNews
,而 latestNewsWithAuthors
的資訊應該通過訂閱 LatestNewsUiState
獲得 。
有一點讓人迷惑的是,官方檔案上有這麼一句話:
Suspend functions in the ViewModel can be useful if instead of exposing state using a stream of data, only a single value needs to be emitted.
對於單發資料的請求允許使用掛起函數返回。但我建議大家忘掉這句話,理由有兩點:
到此這篇關於Android Jetpack架構中ViewModel介面暴露的不合理探究的文章就介紹到這了,更多相關Android ViewModel介面內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45