文章目录
- 效果图
- 使用的依赖版本
- retrofit封装类
- 网络相关数据结构
- 定义协程api
- ViewModel层
- UI层调用
效果图
![kotlin中使用协程Coroutines和retrofit2.6以上搭建MVVM架构_第1张图片](http://img.e-com-net.com/image/info8/cb8a89ddd7784bcca097a69f38325172.jpg)
全文链接
使用的依赖版本
dataBinding {
enabled true
}
implementation 'androidx.core:core-ktx:1.1.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "com.squareup.okhttp3:okhttp:4.2.0"
implementation "com.squareup.retrofit2:retrofit:2.6.1"
implementation "com.squareup.okhttp3:logging-interceptor:4.2.0"
implementation "com.squareup.retrofit2:converter-gson:2.6.1"
// 创建view model
netViewModel = ViewModelProvider(this).get(NetViewMoudle::class.java)
- 废弃过时方式 ViewModelProviders is desprecated
// netViewModel = ViewModelProviders.of(this).get(NetViewMoudle::class.java)
retrofit封装类
package com.wjx.android.wanandroidmvvm.base.https
import android.util.Log
import com.example.myapp.common.Constant
import com.example.myapp.common.SPreference
import com.google.gson.Gson
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.lang.StringBuilder
import java.util.concurrent.TimeUnit
/**
* @Author yangtianfu
* @CreateTime 2020/4/1 21:17
* @Describe retrofit封装类
*/
class RetrofitFactory private constructor() {
private val retrofit: Retrofit
init {
val gson = Gson().newBuilder()
.setLenient()
.serializeNulls()
.create()
retrofit = Retrofit.Builder()
.baseUrl("https://www.wanandroid.com/")
.client(initOkhttpClient())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
companion object {
val instance: RetrofitFactory by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
RetrofitFactory()
}
}
private fun initOkhttpClient(): OkHttpClient {
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.addInterceptor(initLogInterceptor())
.build()
return okHttpClient
}
/*
* 日志拦截器
* */
private fun initLogInterceptor(): HttpLoggingInterceptor {
val interceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
Log.i("Retrofit", message)
}
})
interceptor.level = HttpLoggingInterceptor.Level.BODY
return interceptor
}
/*
* 具体服务实例化
* */
fun getService(service: Class): T {
return retrofit.create(service)
}
}
网络相关数据结构
package com.example.myapp.bean
/**
* @Author yangtianfu
* @CreateTime 2020/4/1 18:08
* @Describe 返回数据最外层包装
*/
data class BaseResp (
var errorCode: Int = 0,
var errorMsg: String = "",
var data: T
)
package com.example.myapp.bean
/**
* @Author yangtianfu
* @CreateTime 2020/4/1 18:13
* @Describe 单个数据返回实体类
*/
data class WBean(
val apkLink: String,
val audit: Int,
val author: String,
val canEdit: Boolean,
val chapterId: Int,
val chapterName: String,
val collect: Boolean,
val courseId: Int,
val desc: String,
val descMd: String,
val envelopePic: String,
val fresh: Boolean,
val id: Int,
val link: String,
val niceDate: String,
val niceShareDate: String,
val origin: String,
val prefix: String,
val projectLink: String,
val publishTime: Long,
val selfVisible: Int,
val shareDate: Long,
val shareUser: String,
val superChapterId: Int,
val superChapterName: String,
val tags: List,
val title: String,
val type: Int,
val userId: Int,
val visible: Int,
val zan: Int
)
package com.example.myapp.ext
import com.example.myapp.bean.BaseResp
/**
* @Author yangtianfu
* @CreateTime 2020/4/1 19:59
* @Describe 数据解析扩展函数 顶层声明 作用域为package
*/
fun BaseResp.dataConvert(): T {
if (errorCode == 0) {
return data
} else {
throw Exception(errorMsg)
}
}
/**
* 全局toast
*/
fun Context.toast(msg: String) {
Toast.makeText(this, msg, LENGTH_SHORT).show()
}
/**
* 全局跳转
*/
fun Activity.openActivity(cls: Class<*>) {
startActivity(Intent(this, cls))
}
定义协程api
package com.wjx.android.wanandroidmvvm.base.https
import com.example.myapp.bean.BaseResp
import com.example.myapp.bean.WBean
import retrofit2.http.GET
/**
* @Author yangtianfu
* @CreateTime 2020/3/31 21:04
* @Describe retrofit 使用协程定义api
*/
interface ApiService {
/**
* 使用协程进行网络请求
*/
@GET("article/top/json/")
suspend fun getTopArticle(): BaseResp>
@GET("article/list/{page}/json")
suspend fun getArticleList(@Path("page") page: Int = 0): Result>
}
ViewModel层
package com.example.myapp.moudle
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapp.bean.WBean
import com.example.myapp.ext.dataConvert
import com.wjx.android.wanandroidmvvm.base.https.ApiService
import com.wjx.android.wanandroidmvvm.base.https.RetrofitFactory
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.lang.Exception
import kotlin.math.log
/**
* @Author yangtianfu
* @CreateTime 2020/4/1 19:18
* @Describe 自带生命周期的viewmoudle
*/
class NetViewMoudle :ViewModel() {
// vm持有数据层引用,并利用livedata赋值更新UI
var list= MutableLiveData>()
/**
* viewModelScope是一个绑定到当前viewModel的作用域 当ViewModel被清除时会自动取消该作用域,所以不用担心内存泄漏为问题
*/
fun getTopArticle(){
viewModelScope.launch {
try {
//withContext表示挂起块 配合Retrofit声明的suspend函数执行 该块会挂起直到里面的网络请求完成 最后一行就是返回值
val data = withContext(Dispatchers.IO){
//dataConvert扩展函数可以很方便的解析出我们想要的数据 接口很多的情况下下可以节省不少无用代码
RetrofitFactory.instance.getService(ApiService::class.java).getTopArticle().dataConvert()
}
//给LiveData赋值 ui会自动更新
list.value = data
}catch (e:Exception){
e.printStackTrace()
Log.e("net error","网络请求错误${e.toString()}")
}
}
}
}
class ArticleViewModel :ViewModel() {
private val _articleListData = MutableLiveData>()
//保证外部只能观察此数据,不同通过setValue修改 model调用articleListData拿到网络请求数据交个观察者,但是不能修改
val articleListData: LiveData> = _articleListData
private val _errorMsg = MutableLiveData()
val errorMsg: LiveData = _errorMsg
fun fetch(page:Int){
viewModelScope.launch {
var result = RetrofitFactory.instance.getService(ApiService::class.java).getArticleList(page)
//请求到的数据用livedata包裹
_articleListData.value = result.data.datas
}
}
}
UI层调用
package com.example.myapp.ui
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import com.example.myapp.R
import com.example.myapp.bean.Animal
import com.example.myapp.databinding.ActivityMainBinding
import com.example.myapp.moudle.NetViewMoudle
import com.example.myapp.vm.AnimalViewModel
import com.google.gson.Gson
import com.wjx.android.wanandroidmvvm.base.https.ApiService
import com.wjx.android.wanandroidmvvm.base.https.RetrofitFactory
import kotlinx.android.synthetic.main.activity_main.*
import kotlin.jvm.java as java
class MainActivity : AppCompatActivity() {
lateinit var mBinding:ActivityMainBinding
lateinit var mViewMode:AnimalViewModel
lateinit var netViewModel:NetViewMoudle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = DataBindingUtil.setContentView(this,R.layout.activity_main)
var animal = Animal("dog",0)
mViewMode = AnimalViewModel(animal)
mBinding.model = mViewMode //model指代xml xml中 name="model"的VM对象
// 创建view model
// netViewModel = ViewModelProviders.of(this).get(NetViewMoudle::class.java)
netViewModel = ViewModelProvider(this).get(NetViewMoudle::class.java)
// 请求网络
mBinding.listener = View.OnClickListener {
when(it.id){
R.id.btn -> netViewModel.getTopArticle()
}
}
// 数据变化更新UI,livedata的数据被观察
netViewModel.list.observe(this, Observer {
tv.text = Gson().toJson(it)
})
}
}
//观察文章列表数据
viewModel.articleListData.observe(this, Observer { list ->
//articleListData 的值改变时触发此监听
loadProgress.visibility = View.GONE
adapter.submitList(list)
})
viewModel.errorMsg.observe(this, Observer {
if (it!=null){
toast(it)
}
})
- github地址:
- https://github.com/BlissYang91/kotlin