github:github.com/wfqdroid/mv…android
先簡單介紹一下上面幾個git
下面介紹一下項目的架構,就以咱們經常使用的Activity爲入口開始介紹github
UI -> ViewModel -> Repository -> NetWork/Dao 箭頭是單項的,也就是說,ViewModel持有Repository的引用,反過來沒有,不然容易內存泄漏。 網絡請求使用的是Retrofit,數據庫Dao層這裏省略了,最近太忙,等有時間再補充上去。Repository(倉庫),負責提供數據,該數據能夠從網絡去取,也能夠從數據庫去取, ViewModel持有Repository的引用,也就是能夠將倉庫的數據拿來本身持有,而後將數據給到UI層。大體的流程就是這樣。 下面咱們說一下項目中的細節:數據庫
先思考一個問題?在項目中,咱們使用協程,當頁面銷燬的時候,咱們怎麼取消請求? 這裏我麼使用ViewModel自帶的viewModelScope.launch,他會在頁面銷燬的時候自動取消請求,不過必需要使用AndroidX,咱們能夠寫一個BaseViewModel編程
open class BaseViewModel:ViewModel() {
fun<T> launch(block: suspend () -> Unit, error: suspend (Throwable) -> Unit, liveData: StateLiveData<T>, isShowLoading:Boolean = true) = viewModelScope.launch {
try {
if(isShowLoading){
liveData.postLoading()
}
block()
} catch (e: Throwable) {
liveData.postError()
error(e)
}finally {
liveData.postSuccess()
}
}
}
class ArticleViewModel(private val respository: ArticleRepository) : BaseViewModel() {
val data = StateLiveData<List<Data>>()
fun getArticle() {
launch({
respository.getArticle()?.let {
data.postValueAndSuccess(it)
}
}, {
}, data)
}
}
複製代碼
StateLiveData是一個由狀態的LiveData,這樣咱們能夠在BaseViewModel的launch裏面直接發送loading等用於界面交互bash
class StateLiveData<T> : MutableLiveData<T>() {
enum class State {
Idle, Loading, Success, Error
}
val state = MutableLiveData<State>()
init {
initState()
}
fun postValueAndSuccess(value: T) {
super.postValue(value)
postSuccess()
}
private fun initState() {
state.postValue(State.Idle)
}
fun postLoading() {
state.postValue(State.Loading)
}
fun postSuccess() {
state.postValue(State.Success)
}
fun postError() {
state.postValue(State.Error)
}
}
複製代碼
咱們如何根據服務端的狀態碼來進行不一樣操做? 這裏咱們採用在Retrofit的Call對象上面擴展了一個await的函數的方式,之後本身的模塊的NetWork直接繼承BaseNetWork便可網絡
open class BaseNetwork {
suspend fun <T> Call<T>.await(): T {
return suspendCoroutine {
enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>, t: Throwable) {
it.resumeWithException(t)
}
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
val body = response.body()
if (body != null) {
body as BaseRes<T>
if (body.errorCode == 0)
it.resume(body)
else if(body.errorCode == 100){
// 比方說登陸超時等
}else{
it.resumeWithException(RuntimeException(body.errorMsg))
}
} else {
it.resumeWithException(RuntimeException("response body is null"))
}
} else {
}
}
})
}
}
}
複製代碼
github:github.com/wfqdroid/mv…架構
參考:juejin.im/post/5ceddb… blog.csdn.net/guolin_blog… developer.android.com/topic/libra…異步