Android單項(xiàng)綁定MVVM項(xiàng)目模板的方法

0.前言

成都創(chuàng)新互聯(lián)公司專注于瑪多網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供瑪多營銷型網(wǎng)站建設(shè),瑪多網(wǎng)站制作、瑪多網(wǎng)頁設(shè)計(jì)、瑪多網(wǎng)站官網(wǎng)定制、成都微信小程序服務(wù),打造瑪多網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供瑪多網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

事情還要從上周和同事的小聚說起,同事說他們公司現(xiàn)在app的架構(gòu)模式用的是MVP模式,但是并沒有通過泛型和繼承等一些列手段強(qiáng)制使用,全靠開發(fā)者在Activity或者Fragment里new一個(gè)presenter來做處理,說白了,全靠開發(fā)者自覺。這引發(fā)了我的一個(gè)思考,程序的架構(gòu)或者設(shè)計(jì)模式的作用,除了傳統(tǒng)的做到低耦合高內(nèi)聚,業(yè)務(wù)分離,我覺得還有一個(gè)更重要的一點(diǎn)就是用來約束開發(fā)者,雖然使用某種模式或者架構(gòu)可能并不會節(jié)省代碼量,有的甚至?xí)黾泳幋a工作,但是讓開發(fā)者在一定規(guī)則內(nèi)進(jìn)行開發(fā),保證一個(gè)一致性,尤其是在當(dāng)一個(gè)項(xiàng)目比較大而且需要團(tuán)隊(duì)合作的前提情況下,就顯得極為重要。前段時(shí)間google公布了jetpack,旨在幫助開發(fā)者更快的構(gòu)建一款app,以此為基礎(chǔ)我寫了這個(gè)項(xiàng)目模板做了一些封裝,來為以后自己寫app的時(shí)候提供一個(gè)支持。

1.什么是MVVM

MVVM這種設(shè)計(jì)模式和MVP極為相似,只不過Presenter換成了ViewModel,而ViewModel是和View相互綁定的。

Android單項(xiàng)綁定MVVM項(xiàng)目模板的方法

MVP

Android單項(xiàng)綁定MVVM項(xiàng)目模板的方法

MVVM

我在項(xiàng)目中并沒有使用這種標(biāo)準(zhǔn)的雙向綁定的MVVM,而是使用了單項(xiàng)綁定的MVVM,通過監(jiān)聽數(shù)據(jù)的變化,來更新UI,當(dāng)UI需要改變是,也是通過改變數(shù)據(jù)后再來改變UI。具體的App架構(gòu)參考了google的官方文檔

Android單項(xiàng)綁定MVVM項(xiàng)目模板的方法

2.框架組合

整個(gè)模板采用了Retrofit+ViewModel+LiveData的這樣組合,Retrofit用來進(jìn)行網(wǎng)絡(luò)請求,ViewModel用來進(jìn)行數(shù)據(jù)存儲于復(fù)用,LiveData用來通知UI數(shù)據(jù)的變化。本篇文章假設(shè)您已經(jīng)熟悉了ViewModel和LiveData。

3.關(guān)鍵代碼分析

3.1Retrofit的處理

首先,網(wǎng)絡(luò)請求我們使用的是Retrofit,Retrofit默認(rèn)返回的是Call,但是因?yàn)槲覀兿M麛?shù)據(jù)的變化是可觀察和被UI感知的,為此需要使用LiveData進(jìn)行對數(shù)據(jù)的包裹,這里不對LiveData進(jìn)行詳細(xì)解釋了,只要記住他是一個(gè)可以在Activity或者Fragment生命周期可以被觀察變化的數(shù)據(jù)結(jié)構(gòu)即可。大家都知道,Retrofit是通過適配器來決定網(wǎng)絡(luò)請求返回的結(jié)果是Call還是什么別的的,為此我們就需要先寫返回結(jié)果的適配器,來返回一個(gè)LiveData

class LiveDataCallAdapterFactory : CallAdapter.Factory() {

 override fun get(
 returnType: Type,
 annotations: Array<Annotation>,
 retrofit: Retrofit
 ): CallAdapter<*, *>? {
 if (CallAdapter.Factory.getRawType(returnType) != LiveData::class.java) {
 return null
 }
 val observableType = CallAdapter.Factory.getParameterUpperBound(0, returnType as ParameterizedType)
 val rawObservableType = CallAdapter.Factory.getRawType(observableType)
 if (rawObservableType != ApiResponse::class.java) {
 throw IllegalArgumentException("type must be a resource")
 }
 if (observableType !is ParameterizedType) {
 throw IllegalArgumentException("resource must be parameterized")
 }
 val bodyType = CallAdapter.Factory.getParameterUpperBound(0, observableType)
 return LiveDataCallAdapter<Any>(bodyType)
 }
}

class LiveDataCallAdapter<R>(private val responseType: Type) : CallAdapter<R, LiveData<ApiResponse<R>>> {


 override fun responseType() = responseType

 override fun adapt(call: Call<R>): LiveData<ApiResponse<R>> {
 return object : LiveData<ApiResponse<R>>() {
 private var started = AtomicBoolean(false)
 override fun onActive() {
 super.onActive()
 if (started.compareAndSet(false, true)) {
  call.enqueue(object : Callback<R> {
  override fun onResponse(call: Call<R>, response: Response<R>) {
  postValue(ApiResponse.create(response))
  }

  override fun onFailure(call: Call<R>, throwable: Throwable) {
  postValue(ApiResponse.create(throwable))
  }
  })
 }
 }
 }
 }
}

首先看LiveDataCallAdapter,這里在adat方法里我們返回了一個(gè)LiveData<ApiResponse<R>> ,ApiResponse是對返回結(jié)果的一層封裝,為什么要封這一層,因?yàn)槲覀兛赡軙W(wǎng)絡(luò)返回的錯(cuò)誤或者一些特殊情況進(jìn)行特殊處理,這些是可以再ApiResponse里做的,然后看LiveDataCallAdapterFactory,返回一個(gè)LiveDataCallAdapter,同時(shí)強(qiáng)制你的接口定義的網(wǎng)絡(luò)請求返回的結(jié)果必需是LiveData<ApiResponse<R>>這種結(jié)構(gòu)。使用的時(shí)候

.object GitHubApi {

 var gitHubService: GitHubService = Retrofit.Builder()
 .baseUrl("https://api.github.com/")
 .addCallAdapterFactory(LiveDataCallAdapterFactory())
 .addConverterFactory(GsonConverterFactory.create())
 .build().create(GitHubService::class.java)


}

interface GitHubService {

 @GET("users/{login}")
 fun getUser(@Path("login") login: String): LiveData<ApiResponse<User>>

}

3.2對ApiResponse的處理

這里用NetWorkResource對返回的結(jié)果進(jìn)行處理,并且將數(shù)據(jù)轉(zhuǎn)換為Resource并包入LiveData傳出去。

abstract class NetWorkResource<ResultType, RequestType>(val executor: AppExecutors) {

 private val result = MediatorLiveData<Resource<ResultType>>()

 init {
 result.value = Resource.loading(null)
 val dbData=loadFromDb()
 if (shouldFetch(dbData)) {
 fetchFromNetWork()
 }
 else{
 setValue(Resource.success(dbData))
 }
 }

 private fun setValue(resource: Resource<ResultType>) {

 if (result.value != resource) {
 result.value = resource
 }

 }

 private fun fetchFromNetWork() {
 val networkLiveData = createCall()

 result.addSource(networkLiveData, Observer {

 when (it) {

 is ApiSuccessResponse -> {
  executor.diskIO().execute {
  val data = processResponse(it)
  executor.mainThread().execute {
  result.value = Resource.success(data)
  }
  }
 }

 is ApiEmptyResponse -> {
  executor.diskIO().execute {
  executor.mainThread().execute {
  result.value = Resource.success(null)
  }
  }
 }

 is ApiErrorResponse -> {
  onFetchFailed()
  result.value = Resource.error(it.errorMessage, null)
 }

 }

 })

 }

 fun asLiveData() = result as LiveData<Resource<ResultType>>

 abstract fun onFetchFailed()

 abstract fun createCall(): LiveData<ApiResponse<RequestType>>

 abstract fun processResponse(response: ApiSuccessResponse<RequestType>): ResultType

 abstract fun shouldFetch(type: ResultType?): Boolean

 abstract fun loadFromDb(): ResultType?
}

這是一個(gè)抽象類,關(guān)注一下它的幾個(gè)抽象方法,這些抽象方法決定了是使用緩存數(shù)據(jù)還是去網(wǎng)路請求以及對網(wǎng)絡(luò)請求返回結(jié)果的處理。其中的AppExecutor是用來處理在主線程更新LiveData,在子線程處理網(wǎng)絡(luò)請求結(jié)果的。

之后只需要在Repository里直接返回一個(gè)匿名內(nèi)部類,復(fù)寫相應(yīng)的抽象方法即可。

class UserRepository {

 private val executor = AppExecutors()

 fun getUser(userId: String): LiveData<Resource<User>> {

 return object : NetWorkResource<User, User>(executor) {
 override fun shouldFetch(type: User?): Boolean {

 return true
 }

 override fun loadFromDb(): User? {
 return null
 }

 override fun onFetchFailed() {

 }

 override fun createCall(): LiveData<ApiResponse<User>> = GitHubApi.gitHubService.getUser(userId)

 override fun processResponse(response: ApiSuccessResponse<User>): User {
 return response.body
 }

 }.asLiveData()
 }
}

3.3對UI的簡單封裝

abstract class VMActivity<T : BaseViewModel> : BaseActivity() {

 protected lateinit var mViewModel: T

 abstract fun loadViewModel(): T

 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 mViewModel = loadViewModel()
 lifecycle.addObserver(mViewModel)
 }
}

這里通過使用集成和泛型,強(qiáng)制開發(fā)者在繼承這個(gè)類時(shí)返回一個(gè)ViewMode。

在使用時(shí)如下。

class MainActivity : VMActivity<MainViewModel>() {
 override fun loadViewModel(): MainViewModel {
 return MainViewModel()
 }

 override fun getLayoutId(): Int = R.layout.activity_main

 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 mViewModel.loginResponseLiveData.observe(this, Observer {

 when (it?.status) {

 Status.SUCCESS -> {
  contentTV.text = it.data?.reposUrl
 }

 Status.ERROR -> {
  contentTV.text = "error"
 }

 Status.LOADING -> {
  contentTV.text = "loading"
 }

 }


 })
 loginBtn.setOnClickListener {
 mViewModel.login("skateboard1991")
 }
 }
}

4.github地址

Github (本地下載)

整個(gè)項(xiàng)目就是一個(gè)Git的獲取用戶信息的一個(gè)簡易demo,還有很多不足,后續(xù)在應(yīng)用過程中會逐漸完善。

5.參考

https://github.com/googlesamples/android-architecture-components

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對創(chuàng)新互聯(lián)的支持。

網(wǎng)站欄目:Android單項(xiàng)綁定MVVM項(xiàng)目模板的方法
分享鏈接:http://muchs.cn/article28/jpghcp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供ChatGPT、品牌網(wǎng)站設(shè)計(jì)軟件開發(fā)、定制網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)、手機(jī)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)