以前項(xiàng)目中雖然也使用MVVM架構(gòu),但由于整體框架不是我自己搭建的妻柒,導(dǎo)致我對(duì)于MVVM架構(gòu)的整體還是很不熟悉概耻,所以這次就自己搭建并實(shí)現(xiàn)一次MVVM架構(gòu)。
MVVM架構(gòu)使用的組件有ViewModel病往、LiveData、ViewBinding/DataBinding等骄瓣,這些組件都是Jetpack庫(kù)中的組件停巷。在使用ViewModel之前要先建立四個(gè)類別的概念:
- ViewModelProcider.Factory:Factory用來生成ViewModel
- ViewModel:持有LiveData,從Repository獲取數(shù)據(jù)榕栏,并向View提供數(shù)據(jù)
- Repository:獲取和處理數(shù)據(jù)畔勤,可以從網(wǎng)絡(luò)、數(shù)據(jù)庫(kù)或其他API獲取并處理數(shù)據(jù)
- LiveData:具有生命周期感知能力的可觀察的數(shù)據(jù)存儲(chǔ)器扒磁,通知View展示數(shù)據(jù)
下圖展示了MVVM架構(gòu)示意圖庆揪,以及相關(guān)組件在其中的作用。
了解了MVVM的基本架構(gòu)和其中各個(gè)組件的作用妨托,可以開始代碼實(shí)現(xiàn)了缸榛。我做這個(gè)項(xiàng)目的初衷是因?yàn)樽罱谡硎占疉ndroid常用的開源庫(kù),為了更方便的展示所實(shí)現(xiàn)的一個(gè)應(yīng)用兰伤。本項(xiàng)目使用Bmob直接作為后臺(tái)數(shù)據(jù)庫(kù)内颗,接入Bmob SDK后調(diào)用API可以直接獲取數(shù)據(jù),以此來模擬后臺(tái)接口敦腔。同時(shí)本項(xiàng)目使用Koin作為依賴注入的框架起暮,省去初始化ViewModel、Repository会烙、ViewModelProcider.Factory的過程。
先貼上項(xiàng)目目錄筒捺,需要關(guān)注的是高亮顯示的文件(使用Koin省去了Factory類的實(shí)現(xiàn)):
-
ViewModel類:
實(shí)現(xiàn)HomeViewModel類柏腻,需要繼承繼承自ViewModel(),作為HomeFragment的ViewModel系吭。HomeViewModel類的構(gòu)造參數(shù)是BmobRepository五嫂,類中有一個(gè)LiveData變量用來承載數(shù)據(jù),一個(gè)函數(shù)getAllRecommendLibrary()獲取開源庫(kù)數(shù)據(jù)肯尺,函數(shù)實(shí)現(xiàn)是repository在協(xié)程中獲取云數(shù)據(jù)庫(kù)中的數(shù)據(jù):
class HomeViewModel(private val repository: BmobRepository) : ViewModel() { var libraryRecommendData = MutableLiveData<MutableList<AndroidLibrary>>() fun getAllRecommendLibrary() { viewModelScope.launch { repository.getAllRecommendLibrary(libraryRecommendData) } } }
-
Repository類:
實(shí)現(xiàn)BmobRepository類沃缘,作為HomeViewModel的數(shù)據(jù)提供方。BmobRepository類中有一個(gè)掛起函數(shù)getAllRecommendLibrary(libraryRecommendData: MutableLiveData<MutableList>)用來獲取云數(shù)據(jù)庫(kù)中的數(shù)據(jù)则吟,函數(shù)的參數(shù)是LiveData槐臀,在獲取數(shù)據(jù)后,利用setValue通知View展示數(shù)據(jù)氓仲。
class BmobRepository { /** * 獲取Bmob中所有推薦開源項(xiàng)目 */ suspend fun getAllRecommendLibrary(libraryRecommendData: MutableLiveData<MutableList<AndroidLibrary>>) { return withContext(Dispatchers.IO) { val bombQuery: BmobQuery<AndroidLibrary> = BmobQuery() bombQuery.findObjects(object : FindListener<AndroidLibrary>() { override fun done(data: MutableList<AndroidLibrary>?, ex: BmobException?) { if (ex == null) { Timber.d("Bmob find success") libraryRecommendData.value = data!! } else { Timber.d("Bmob exception $ex") } } }) } } }
-
Koin初始化:
Koin的初始化分為兩步:
定義ViewModel水慨,告訴Kioin從哪里找到ViewModel和Repository并自動(dòng)生成得糜,這里我選擇直接寫在BaseApplication中,需要注意的是需要定義在最外層晰洒,即和Classt同級(jí):
-
在Application的onCreate()函數(shù)中初始化Koin:
class BaseApplication : Application() { override fun onCreate() { super.onCreate() //初始化Bmob Bmob.initialize(this, Constant.BMOB_APP_ID) //初始化Timber if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) } //第二步: startKoin { //Android context androidContext(this@BaseApplication) //modules val list = listOf(myModule, repoModel) modules(list) } } } //第一步: //定義一個(gè)myModule作為Viewmodel val myModule = module { viewModel { HomeViewModel(get()) } } //定義一個(gè)repoModule val repoModel = module { single { BmobRepository() } }
Fragment類實(shí)現(xiàn):
實(shí)現(xiàn)HomeFragment類作為視圖層朝抖,其中分為兩步:
-
變量homeViewModel作為ViewModel獲取數(shù)據(jù),使用Koin后的初始化方式十分簡(jiǎn)單
private val homeViewModel: HomeViewModel by viewModel()//懶加載初始化
-
LiveData注冊(cè)監(jiān)聽ViewModel中的數(shù)據(jù)改變谍珊,并實(shí)現(xiàn)獲取數(shù)據(jù)后的操作
private fun initRegister() { //LiveData在視圖層中注冊(cè)監(jiān)聽后治宣,在ViewModel中的數(shù)據(jù)改變時(shí)可以持續(xù)收到數(shù)據(jù) homeViewModel.libraryRecommendData.observe(viewLifecycleOwner, { Timber.d("t $it") (binding.rvAndroidLibrary.adapter as AndroidLibraryAdapter).apply { data = it notifyDataSetChanged() } }) }
-
ViewModel調(diào)用函數(shù)通知Repository去查詢數(shù)據(jù):
override fun onResume() { super.onResume() homeViewModel.getAllRecommendLibrary() }
自此,一個(gè)MVVM架構(gòu)的應(yīng)用搭建完成砌滞,第一次獨(dú)立的搭建MVVM架構(gòu)之后侮邀,對(duì)于MVVM架構(gòu)的理解加深了不少,對(duì)于JetPack庫(kù)中的組件和其它開源庫(kù)也有了新的認(rèn)識(shí)布持,此外MVVM架構(gòu)還經(jīng)常和Retrofit豌拙、RxJava等開源庫(kù)配合使用,希望以后有機(jī)會(huì)可以再進(jìn)行實(shí)踐操作L馀按傅!
本項(xiàng)目使用開源組件庫(kù):koin、timber胧卤、permissionx唯绍、BaseRecyclerViewAdapterHelper