Android開發(fā)架構(gòu)
如果開發(fā)過程中大家各自為戰(zhàn)层坠,沒有統(tǒng)一規(guī)范,久而久之买猖,項(xiàng)目代碼會(huì)變得混亂且后續(xù)難以維護(hù)改橘。當(dāng)使用統(tǒng)一的架構(gòu)模式后,有很多的好處玉控,如:
- 統(tǒng)一開發(fā)規(guī)范飞主,使得代碼整潔、規(guī)范高诺,后續(xù)易于維護(hù)及擴(kuò)展
- 提高開發(fā)效率(尤其在團(tuán)隊(duì)人員較多時(shí))
- 模塊單一職責(zé)碌识,使得模塊專注自己內(nèi)部(面向?qū)ο?,模塊間解耦
總之懒叛,開發(fā)架構(gòu)是前人總結(jié)出來(lái)的一套行之有效的開發(fā)模式丸冕,目的是達(dá)到高內(nèi)聚耽梅,低耦合的效果薛窥,使得項(xiàng)目代碼更健壯、易維護(hù)眼姐。
Android中常見的架構(gòu)模式有MVC(Model-View-Controller)
诅迷、MVP(Model-View-Presenter)
、MVVM(Model-View-ViewModel)
众旗,一起來(lái)看下各自的特點(diǎn):
MVC
MVC(Model-View-Controller)
是比較早期的架構(gòu)模式罢杉,模式整體也比較簡(jiǎn)單。
MVC模式將程序分成了三個(gè)部分:
- Model模型層:業(yè)務(wù)相關(guān)的數(shù)據(jù)(如網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)贡歧、本地?cái)?shù)據(jù)庫(kù)數(shù)據(jù)等)及其對(duì)數(shù)據(jù)的處理
- View視圖層:頁(yè)面視圖(通過XML布局編寫視圖層)滩租,負(fù)責(zé)接收用戶輸入、發(fā)起數(shù)據(jù)請(qǐng)求及展示結(jié)果頁(yè)面
- Controller控制器層:M與V之間的橋梁利朵,負(fù)責(zé)業(yè)務(wù)邏輯
MVC特點(diǎn):
-
簡(jiǎn)單易用:上圖表述了數(shù)據(jù)整個(gè)流程:
View
接收用戶操作律想,通過Controller
去處理業(yè)務(wù)邏輯,并通過Model
去獲取/更新數(shù)據(jù)绍弟,然后Model層
又將最新的數(shù)據(jù)傳回View
層進(jìn)行頁(yè)面展示技即。 - 架構(gòu)簡(jiǎn)單的另一面往往是對(duì)應(yīng)的副作用:由于XML布局能力弱,我們的View層的很多操作都是寫在Activity/Fragment中樟遣,同時(shí)而叼,Controller身笤、Model層的代碼也大都寫在Activity/Fragment中,這就會(huì)導(dǎo)致一個(gè)問題葵陵,當(dāng)業(yè)務(wù)邏輯比較復(fù)雜時(shí)液荸,Activity/Fragment中的代碼量會(huì)很大,其違背了類單一職責(zé)脱篙,不利于后續(xù)擴(kuò)展及維護(hù)莹弊。尤其是后期你剛接手的項(xiàng)目,一個(gè)Activity/Fragment類中的代碼動(dòng)輒上千行代碼涡尘,那感覺著實(shí)酸爽:
當(dāng)然忍弛,如果業(yè)務(wù)很簡(jiǎn)單,使用MVC模式還是一種不錯(cuò)的選擇考抄。
MVP
MVP(Model-View-Presenter)
细疚,架構(gòu)圖如下:
MVP各模塊職責(zé)如下:
- Model模型:業(yè)務(wù)相關(guān)的數(shù)據(jù)(如網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)、本地?cái)?shù)據(jù)庫(kù)數(shù)據(jù)等)及其對(duì)數(shù)據(jù)的處理
- View視圖:頁(yè)面視圖(Activity/Fragment)川梅,負(fù)責(zé)接收用戶輸入疯兼、發(fā)起數(shù)據(jù)請(qǐng)求及展示結(jié)果頁(yè)面
- Presenter:M與V之間的橋梁,負(fù)責(zé)業(yè)務(wù)邏輯
MVP特點(diǎn): View
層接收用戶操作贫途,并通過持有的Presenter
去處理業(yè)務(wù)邏輯吧彪,請(qǐng)求數(shù)據(jù);接著Presenter
層通過Model
去獲取數(shù)據(jù)丢早,然后Model
又將最新的數(shù)據(jù)傳回Presenter
層姨裸,Presenter
層又持有View
層的引用,進(jìn)而將數(shù)據(jù)傳給View
層進(jìn)行展示怨酝。
MVP相比MVC的幾處變化:
- View層與Model層不再交互傀缩,而是通過Presenter去進(jìn)行聯(lián)系
- 本質(zhì)上MVP是面向接口編程,Model/View/Presenter每層的職責(zé)分工明確农猬,當(dāng)業(yè)務(wù)復(fù)雜時(shí)赡艰,整個(gè)流程邏輯也是很清晰的
當(dāng)然,MVP
也不是十全十美的斤葱,MVP
本身也存在以下問題:
-
View
層會(huì)抽象成IView
接口慷垮,并在IView
中聲明一些列View
相關(guān)的方法;同樣的揍堕,Presenter
會(huì)被抽象成IPresenter
接口及其一些列方法料身,每當(dāng)實(shí)現(xiàn)一個(gè)功能時(shí),都需要編寫多個(gè)接口及其對(duì)應(yīng)的方法鹤啡,實(shí)現(xiàn)起來(lái)相對(duì)比較繁瑣惯驼,而且每次有改動(dòng)時(shí),對(duì)應(yīng)的接口方法也基本都會(huì)再去改動(dòng)。 -
View
層與Presenter
層相互持有祟牲,當(dāng)View
層關(guān)閉時(shí)隙畜,由于Presenter
層不是生命周期感知的,可能會(huì)導(dǎo)致內(nèi)存泄漏甚至是崩潰说贝。
ps:如果你的項(xiàng)目中使用了RxJava
议惰,可以使用 AutoDispose 自動(dòng)解綁。
MVVM
MVVM(Model-View-ViewModel)
乡恕,架構(gòu)圖如下:
MVVM各職責(zé)如下:
- Model模型:業(yè)務(wù)相關(guān)的數(shù)據(jù)(如網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)言询、本地?cái)?shù)據(jù)庫(kù)數(shù)據(jù)等)及其對(duì)數(shù)據(jù)的處理
- View視圖:頁(yè)面視圖(Activity/Fragment),負(fù)責(zé)接收用戶輸入傲宜、發(fā)起數(shù)據(jù)請(qǐng)求及展示結(jié)果頁(yè)面
- ViewModel:M與V之間的橋梁簇爆,負(fù)責(zé)業(yè)務(wù)邏輯
MVVM特點(diǎn):
- View層接收用戶操作炸宵,并通過持有的ViewModel去處理業(yè)務(wù)邏輯遮晚,請(qǐng)求數(shù)據(jù)钉稍;
- ViewModel層通過Model去獲取數(shù)據(jù),然后Model又將最新的數(shù)據(jù)傳回ViewModel層报嵌,到這里虱咧,ViewModel與Presenter所做的事基本是一樣的。但是ViewModel不會(huì)也不能持有View層的引用锚国,而是View層會(huì)通過觀察者模式監(jiān)聽ViewModel層的數(shù)據(jù)變化腕巡,當(dāng)有新數(shù)據(jù)時(shí),View層能自動(dòng)收到新數(shù)據(jù)并刷新界面血筑。
UI驅(qū)動(dòng) vs 數(shù)據(jù)驅(qū)動(dòng)
MVP
中绘沉,Presenter
中需要持有View
層的引用,當(dāng)數(shù)據(jù)變化時(shí)云挟,需要主動(dòng)調(diào)用View
層對(duì)應(yīng)的方法將數(shù)據(jù)傳過去并進(jìn)行UI刷新梆砸,這種可以認(rèn)為是UI驅(qū)動(dòng)转质;而MVVM
中园欣,ViewModel
并不會(huì)持有View
層的引用,View
層會(huì)監(jiān)聽數(shù)據(jù)變化休蟹,當(dāng)ViewModel
中有數(shù)據(jù)更新時(shí)沸枯,View
層能直接拿到新數(shù)據(jù)并完成UI更新,這種可以認(rèn)為是數(shù)據(jù)驅(qū)動(dòng)赂弓,顯然绑榴,MVVM
相比于MVP
來(lái)說(shuō)更加解耦。
MVVM的具體實(shí)現(xiàn)
上面介紹了MVC/MVP/MVVM
的各自特點(diǎn)盈魁,其中MVC/MVP
的具體使用方式翔怎,本文不再展開實(shí)現(xiàn),接下來(lái)主要聊一下MVVM
的使用及封裝,MVVM
也是官方推薦的架構(gòu)模式赤套。
Jetpack MVVM
Jetpack
是官方推出的一系列組件庫(kù)飘痛,使用組件庫(kù)開發(fā)有很多好處,如:
- 遵循最佳做法:采用最新的設(shè)計(jì)方法構(gòu)建容握,具有向后兼容性宣脉,可以減少崩潰和內(nèi)存泄漏
- 消除樣板代碼:開發(fā)者可以更好地專注業(yè)務(wù)邏輯
- 減少不一致:可以在各種Android版本中運(yùn)行,兼容性更好剔氏。
為了實(shí)現(xiàn)上面的MVVM
架構(gòu)模式塑猖,Jetpack
提供了多個(gè)組件來(lái)實(shí)現(xiàn),具體來(lái)說(shuō)有Lifecycle谈跛、LiveData羊苟、ViewModel(這里的ViewModel是MVVM中ViewModel層的具體實(shí)現(xiàn))
,其中Lifecycle
負(fù)責(zé)生命周期相關(guān)感憾;LiveData
賦予類可觀察践险,同時(shí)還是生命周期感知的(內(nèi)部使用了Lifecycle
);ViewModel
旨在以注重生命周期的方式存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù)吹菱,針對(duì)這幾個(gè)庫(kù)的詳細(xì)介紹及使用方式不再展開巍虫,有興趣的可以參見前面的文章:
通過這幾個(gè)庫(kù),就可以實(shí)現(xiàn)MVVM
了鳍刷,官方也發(fā)布了MVVM
的架構(gòu)圖:
其中Activity/Fragment
為View
層占遥,ViewModel+LiveData
為ViewModel
層,為了統(tǒng)一管理網(wǎng)絡(luò)數(shù)據(jù)及本地?cái)?shù)據(jù)數(shù)據(jù)输瓜,又引入了Repository
中間管理層瓦胎,本質(zhì)上是為了更好地管理數(shù)據(jù),為了簡(jiǎn)單把他們統(tǒng)稱為Model
層吧尤揣。
使用舉例
- View層代碼:
//MvvmExampleActivity.kt
class MvvmExampleActivity : BaseActivity() {
private val mTvContent: TextView by id(R.id.tv_content)
private val mBtnQuest: Button by id(R.id.btn_request)
private val mToolBar: Toolbar by id(R.id.toolbar)
override fun getLayoutId(): Int {
return R.layout.activity_wan_android
}
override fun initViews() {
initToolBar(mToolBar, "Jetpack MVVM", true)
}
override fun init() {
//獲取ViewModel實(shí)例搔啊,注意這里不能直接new,因?yàn)閂iewModel的生命周期比Activity長(zhǎng)
mViewModel = ViewModelProvider(this).get(WanViewModel::class.java)
mBtnQuest.setOnClickListener {
//請(qǐng)求數(shù)據(jù)
mViewModel.getWanInfo()
}
//ViewModel中的LiveData注冊(cè)觀察者并監(jiān)聽數(shù)據(jù)變化
mViewModel.mWanLiveData.observe(this) { list ->
val builder = StringBuilder()
for (index in list.indices) {
//每條數(shù)據(jù)進(jìn)行折行顯示
if (index != list.size - 1) {
builder.append(list[index])
builder.append("\n\n")
} else {
builder.append(list[index])
}
}
mTvContent.text = builder.toString()
}
}
}
- ViewModel層代碼:
//WanViewModel.kt
class WanViewModel : ViewModel() {
//LiveData
val mWanLiveData = MutableLiveData<List<WanModel>>()
//loading
val loadingLiveData = SingleLiveData<Boolean>()
//異常
val errorLiveData = SingleLiveData<String>()
//Repository中間層 管理所有數(shù)據(jù)來(lái)源 包括本地的及網(wǎng)絡(luò)的
private val mWanRepo = WanRepository()
fun getWanInfo(wanId: String = "") {
//展示Loading
loadingLiveData.postValue(true)
viewModelScope.launch(Dispatchers.IO) {
try {
val result = mWanRepo.requestWanData(wanId)
when (result.state) {
State.Success -> mWanLiveData.postValue(result.data)
State.Error -> errorLiveData.postValue(result.msg)
}
} catch (e: Exception) {
error(e.message ?: "")
} finally {
loadingLiveData.postValue(false)
}
}
}
}
- Repository層(Model層)代碼:
class WanRepository {
//請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)
suspend fun requestWanData(drinkId: String): BaseData<List<WanModel>> {
val service = RetrofitUtil.getService(DrinkService::class.java)
val baseData = service.getBanner()
if (baseData.code == 0) {
//正確
baseData.state = State.Success
} else {
//錯(cuò)誤
baseData.state = State.Error
}
return baseData
}
}
這里只通過Retrofit
請(qǐng)求了網(wǎng)絡(luò)數(shù)據(jù) 玩Android 開放API北戏,如果需要添加本地?cái)?shù)據(jù)负芋,只需要在方法里添加本地?cái)?shù)據(jù)處理即可,即 Repository是數(shù)據(jù)的管理中間層嗜愈,對(duì)數(shù)據(jù)進(jìn)行統(tǒng)一管理旧蛾,ViewModel層中不需要關(guān)心數(shù)據(jù)的來(lái)源,大家各司其職即可蠕嫁,符合單一職責(zé)锨天,代碼可讀性更好,同時(shí)也更加解耦剃毒。在View
層點(diǎn)擊按鈕請(qǐng)求數(shù)據(jù)病袄,執(zhí)行結(jié)果如下:
MVP
,MVVM
既不用聲明多個(gè)接口及方法益缠,同時(shí)ViewModel
也不會(huì)像Presenter
那樣去持有View
層的引用厂镇,而是生命周期感知的,MVVM
方式更加解耦左刽。
封裝
上一節(jié)介紹了Jetpack MVVM的使用例子捺信,可以看到有一些代碼邏輯是可以抽離出來(lái)封裝到公共部分的,那么本節(jié)就嘗試對(duì)其做一次封裝欠痴。
首先迄靠,請(qǐng)求數(shù)據(jù)時(shí)可能會(huì)展示Loading
,請(qǐng)求完后可能是空數(shù)據(jù)喇辽、錯(cuò)誤數(shù)據(jù)掌挚,對(duì)應(yīng)下面的IStatusView
接口聲明:
interface IStatusView {
fun showEmptyView() //空視圖
fun showErrorView(errMsg: String) //錯(cuò)誤視圖
fun showLoadingView(isShow: Boolean) //展示Loading視圖
}
因?yàn)閂iewModel是在Activity中初始化的,所以可以封裝成一個(gè)Base類:
abstract class BaseMvvmActivity<VM : BaseViewModel> : BaseActivity(), IStatusView {
protected lateinit var mViewModel: VM
protected lateinit var mView: View
private lateinit var mLoadingDialog: LoadingDialog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mLoadingDialog = LoadingDialog(this, false)
mViewModel = getViewModel()!!
init()
registerEvent()
}
/**
* 獲取ViewModel 子類可以復(fù)寫菩咨,自行初始化
*/
protected open fun getViewModel(): VM? {
//當(dāng)前對(duì)象超類的Type
val type = javaClass.genericSuperclass
//ParameterizedType表示參數(shù)化的類型
if (type != null && type is ParameterizedType) {
//返回此類型實(shí)際類型參數(shù)的Type對(duì)象數(shù)組
val actualTypeArguments = type.actualTypeArguments
val tClass = actualTypeArguments[0]
return ViewModelProvider(this).get(tClass as Class<VM>)
}
return null
}
override fun showLoadingView(isShow: Boolean) {
if (isShow) {
mLoadingDialog.showDialog(this, false)
} else {
mLoadingDialog.dismissDialog()
}
}
override fun showEmptyView() {
......
}
//錯(cuò)誤視圖 并且可以重試
override fun showErrorView(errMsg: String) {
.......
}
private fun registerEvent() {
//接收錯(cuò)誤信息
mViewModel.errorLiveData.observe(this) { errMsg ->
showErrorView(errMsg)
}
//接收Loading信息
mViewModel.loadingLiveData.observe(this, { isShow ->
showLoadingView(isShow)
})
}
abstract fun init()
}
Base
類中初始化ViewModel
吠式,還可以通過官方activity-ktx
、fragment-ktx
擴(kuò)展庫(kù)抽米,初始化方式:val model: VM by viewModels()
特占。
子類中繼承如下:
class MvvmExampleActivity : BaseMvvmActivity<WanViewModel>() {
private val mTvContent: TextView by id(R.id.tv_content)
private val mBtnQuest: Button by id(R.id.btn_request)
private val mToolBar: Toolbar by id(R.id.toolbar)
override fun getLayoutId(): Int {
return R.layout.activity_wan_android
}
override fun initViews() {
initToolBar(mToolBar, "Jetpack MVVM", true)
}
override fun init() {
mBtnQuest.setOnClickListener {
//請(qǐng)求數(shù)據(jù)
mViewModel.getWanInfo()
}
/**
* 這里使用了擴(kuò)展函數(shù),等同于mViewModel.mWanLiveData.observe(this) {}
*/
observe(mViewModel.mWanLiveData) { list ->
val builder = StringBuilder()
for (index in list.indices) {
//每條數(shù)據(jù)進(jìn)行折行顯示
if (index != list.size - 1) {
builder.append(list[index])
builder.append("\n\n")
} else {
builder.append(list[index])
}
}
mTvContent.text = builder.toString()
}
}
}
我們把ViewModel的初始化放到了父類里進(jìn)行云茸,代碼看上去更簡(jiǎn)單了是目。監(jiān)聽數(shù)據(jù)變化mViewModel.mWanLiveData.observe(this) {}
方式改成observe(mViewModel.mWanLiveData) {}
方式,少傳了一個(gè)LifecycleOwner标捺,其實(shí)這是一個(gè)擴(kuò)展函數(shù)懊纳,如下:
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, observer: (t: T) -> Unit) {
liveData.observe(this, { observer(it) })
}
ps:我們初始化View控件時(shí),如 private val mBtnQuest: Button by id(R.id.btn_request)
亡容,依然使用了擴(kuò)展函數(shù)嗤疯,如下:
fun <T : View> Activity.id(id: Int) = lazy {
findViewById<T>(id)
}
不用像寫java代碼中那樣時(shí)刻要想著判空,同時(shí)只會(huì)在使用時(shí)才會(huì)進(jìn)行初始化闺兢,很實(shí)用茂缚!
說(shuō)回來(lái),接著是ViewModel層的封裝列敲,BaseViewModel.kt
:
abstract class BaseViewModel : ViewModel() {
//loading
val loadingLiveData = SingleLiveData<Boolean>()
//異常
val errorLiveData = SingleLiveData<String>()
/**
* @param request 正常邏輯
* @param error 異常處理
* @param showLoading 請(qǐng)求網(wǎng)絡(luò)時(shí)是否展示Loading
*/
fun launchRequest(
showLoading: Boolean = true,
error: suspend (String) -> Unit = { errMsg ->
//默認(rèn)異常處理阱佛,子類可以進(jìn)行覆寫
errorLiveData.postValue(errMsg)
}, request: suspend () -> Unit
) {
//是否展示Loading
if (showLoading) {
loadStart()
}
//使用viewModelScope.launch開啟協(xié)程
viewModelScope.launch(Dispatchers.IO) {
try {
request()
} catch (e: Exception) {
error(e.message ?: "")
} finally {
if (showLoading) {
loadFinish()
}
}
}
}
private fun loadStart() {
loadingLiveData.postValue(true)
}
private fun loadFinish() {
loadingLiveData.postValue(false)
}
}
擴(kuò)展一下: 1、上面執(zhí)行網(wǎng)絡(luò)請(qǐng)求時(shí)戴而,使用viewModelScope.launch來(lái)啟動(dòng)協(xié)程,引入方式:
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
這樣就可以直接在ViewModel中啟動(dòng)協(xié)程并且當(dāng)ViewModel生命周期結(jié)束時(shí)協(xié)程也會(huì)自動(dòng)關(guān)閉翩蘸,避免使用GlobalScope.launch { }
或MainScope().launch { }
還需自行關(guān)閉協(xié)程所意, 當(dāng)然,如果是在Activity/Fragment、liveData中使用協(xié)程扶踊,也可以按需引入:
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
具體可以參見官方的 將 Kotlin 協(xié)程與生命周期感知型組件一起使用 這篇文章泄鹏。
2、另外細(xì)心的讀者可能觀察到秧耗,上面我們的Loading备籽、Error信息監(jiān)聽都是用的SingleLiveData,把這個(gè)類打代碼貼一下:
/**
* 多個(gè)觀察者存在時(shí)分井,只有一個(gè)Observer能夠收到數(shù)據(jù)更新
* https://github.com/android/architecture-samples/blob/dev-todo-mvvm-live/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/SingleLiveEvent.java
*/
class SingleLiveData<T> : MutableLiveData<T>() {
companion object {
private const val TAG = "SingleLiveEvent"
}
private val mPending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(owner) { t ->
//如果expect為true车猬,那么將值update為false,方法整體返回true尺锚,
//即當(dāng)前Observer能夠收到更新珠闰,后面如果還有訂閱者,不能再收到更新通知了
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
}
}
override fun setValue(@Nullable value: T?) {
//AtomicBoolean中設(shè)置的值設(shè)置為true
mPending.set(true)
super.setValue(value)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
}
可以看到SingleLiveData還是繼承自MutableLiveData瘫辩,區(qū)別是當(dāng)多個(gè)觀察者存在時(shí)伏嗜,只有一個(gè)Observer能夠收到數(shù)據(jù)更新,本質(zhì)上是在observe()時(shí)通過CAS加了限制伐厌,注釋已經(jīng)很詳細(xì)了承绸,不再贅述。
子類中繼承如下:
class WanViewModel : BaseViewModel() {
//LiveData
val mWanLiveData = MutableLiveData<List<WanModel>>()
//Repository中間層 管理所有數(shù)據(jù)來(lái)源 包括本地的及網(wǎng)絡(luò)的
private val mWanRepo = WanRepository()
fun getWanInfo(wanId: String = "") {
launchRequest {
val result = mWanRepo.requestWanData(wanId)
when (result.state) {
State.Success -> mWanLiveData.postValue(result.data)
State.Error -> errorLiveData.postValue(result.msg)
}
}
}
}
最后是對(duì)Model層的封裝挣轨,BaseRepository.kt
:
open class BaseRepository {
suspend fun <T : Any> executeRequest(
block: suspend () -> BaseData<T>
): BaseData<T> {
val baseData = block.invoke()
if (baseData.code == 0) {
//正確
baseData.state = State.Success
} else {
//錯(cuò)誤
baseData.state = State.Error
}
return baseData
}
}
數(shù)據(jù)基類BaseData.kt
:
class BaseData<T> {
@SerializedName("errorCode")
var code = -1
@SerializedName("errorMsg")
var msg: String? = null
var data: T? = null
var state: State = State.Error
}
enum class State {
Success, Error
}
子類中繼承如下:
class WanRepository : BaseRepository() {
suspend fun requestWanData(drinkId: String): BaseData<List<WanModel>> {
val service = RetrofitUtil.getService(DrinkService::class.java)
return executeRequest {
service.getBanner()
}
}
}
到此八酒,基本上就完成了,代碼中有一些細(xì)節(jié)沒貼出來(lái)刃唐,完整代碼參見:Jetpack MVVM
參考
【1】Android應(yīng)用架構(gòu)指南
【2】www.php.cn/faq/417265.…
相關(guān)推薦
Android架構(gòu)模式實(shí)戰(zhàn)-MVC羞迷、MVP、MVVM+jetpack_嗶哩嗶哩_bilibili
Android項(xiàng)目實(shí)戰(zhàn)—MVVM與Jetpack架構(gòu)核心技術(shù)databinding解密_嗶哩嗶哩_bilibili
Android開發(fā)項(xiàng)目實(shí)戰(zhàn)——jetpack與MVC画饥、MVP衔瓮、MVVM的項(xiàng)目實(shí)戰(zhàn)
本文轉(zhuǎn)自 https://juejin.cn/post/7010954321785257997,如有侵權(quán)抖甘,請(qǐng)聯(lián)系刪除热鞍。