Android:Retrofit+Kotlin协程简单使用(MVVM架构模式)


MVP架构请求请看: 上一篇

MVVM架构模式(Jetpack)

1. 引入lib,APP模块:build.gradle

dependencies {
	// 添加Jetpack中架构组件的依赖,注意viewmodel要添加viewmodel-ktx的依赖
    api "androidx.lifecycle:lifecycle-livedata:${rootProject.ext.lifecycle}"
    api "androidx.lifecycle:lifecycle-viewmodel-ktx:${rootProject.ext.lifecycle}"
    api "androidx.lifecycle:lifecycle-extensions:${rootProject.ext.lifecycle}"
    }

2. ViewModel扩展类

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

/**
 * ViewModel扩展方法:启动协程
 * @param block 协程逻辑
 * @param onError 错误回调方法
 * @param onComplete 完成回调方法
 */
fun ViewModel.launch(
        block: suspend CoroutineScope.() -> Unit,
        onError: (e: Throwable) -> Unit = {},
        onComplete: () -> Unit = {}
) {
    viewModelScope.launch(
            CoroutineExceptionHandler { _, throwable ->
                run {
                    // 这里统一处理错误
                    ExceptionUtil.catchException(throwable)
                    onError(throwable)
                }
            }
    ) {
        try {
            block.invoke(this)
        } finally {
            onComplete()
        }
    }
}

/**
 * ViewModel扩展属性:加载状态
 */
val ViewModel.loadState: MutableLiveData<LoadState>
    get() = MutableLiveData()

3. LoadState.kt

/**
 * 加载状态
 * @author ssq
 * sealed 关键字表示此类仅内部继承
 */
sealed class LoadState(val msg: String) {
    /**
     * 加载中
     */
    class Loading(msg: String = ""): LoadState(msg)

    /**
     * 成功
     */
    class Success(msg: String = ""): LoadState(msg)

    /**
     * 失败
     */
    class Fail(msg: String = ""): LoadState(msg)
}

4. Activity基类

import android.os.Bundle
import android.widget.Toast
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProviders

/**
 * Activity 基类
 * MV ViewModel 架构
 * @author ssq
 */
abstract class BaseViewModelActivity<VM : ViewModel> : : AppCompatActivity(), CoroutineScope by MainScope() {
    protected lateinit var mViewModel: VM

    protected abstract fun getLayoutId(): Int

    /**
     * 提供ViewModel类
     */
    protected abstract fun providerVMClass(): Class<VM>?

    protected abstract fun initView()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(getLayoutId())
        providerVMClass()?.let { mViewModel = ViewModelProviders.of(this).get(it) }
        initView()
    }
    
	override fun onDestroy() {
        cancel()// 取消协程
        super.onDestroy()
    }

    protected open fun showFail(message: String? = null) {
        message?.let { Toast.makeText(this, it, Toast.LENGTH_LONG).show() }
    }
}

5. ViewModel类

import android.content.Context
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class MyActivityVM: ViewModel() {
	val myData = MutableLiveData<MyData>()
	/**
     * 获取用户信息
     */
    fun getData(context: Context?) = launch({
        if (!MyUtil.validateLogin()) return@launch
        loadState.value = LoadState.Loading()
        myData.value = Repository.getMyData(context)
        loadState.value = LoadState.Success()
    }, {
        loadState.value = LoadState.Fail()
    })
}

6. Repository.kt

object Repository {
	// 接口API服务
    private val api by lazy { ApiFactory.createService(ApiConstant.BASE_URL, ApiService::class.java) }

	/**
     * 预处理数据(错误)
     * @param context 跳至登录页的上下文
     */
    private suspend fun <T : BaseBean> preprocessingData(baseBean: T, context: Context? = null): T = if (baseBean.code == 0) {// 成功
        // 返回数据
        baseBean
    } else {// 失败
        // 验证登录是否过期
        validateCode(context, baseBean.code)
        // 抛出接口异常 ApiException是自定义的异常类
        throw ApiException(baseBean.code, baseBean.message)
    }
    // HashMap() 是请求的参数
 	suspend fun getMyInfo(context: Context? = null): MyData = api.getMyData(HashMap())
    ).let {
        preprocessingData(it, context)
    }
}

7. ApiFactory.kt

/**
 * 接口请求工厂
 * @author ssq
 */
object ApiFactory {
	// OkHttpClient客户端
    private val mClient: OkHttpClient by lazy { newClient() }
	/**
     * 创建API Service接口实例
     */
    fun <T> createService(baseUrl: String, clazz: Class<T>): T = Retrofit.Builder().baseUrl(baseUrl).client(mClient)
            .addConverterFactory(GsonConverterFactory.create(Gson.getInstance()))
            .build().create(clazz)

    /**
     * OkHttpClient客户端
     */
    private fun newClient(): OkHttpClient = OkHttpClient.Builder().apply {
        connectTimeout(30, TimeUnit.SECONDS)// 连接时间:30s超时
        readTimeout(10, TimeUnit.SECONDS)// 读取时间:10s超时
        writeTimeout(10, TimeUnit.SECONDS)// 写入时间:10s超时
    }.build()
}

8. ApiService.kt

/**
 * 网络服务接口(协程)
 * @author ssq
 * @JvmSuppressWildcards 用来注解类和方法,使得被标记元素的泛型参数不会被编译成通配符?
 */
@JvmSuppressWildcards
interface ApiService {

    /**
     * 异步请求数据
     */
    @FormUrlEncoded
    @POST("app/my")
    suspend fun getMyData(@FieldMap map: Map<String, Any>): MyData
}

9. MyActivity.kt

class MyActivity : BaseViewModelActivity<MyActivityVM>() {
	override fun getLayoutId(): Int = R.layout.activity_my

    override fun providerVMClass(): Class<MyActivityVM>? = MyActivityVM::class.java

    override fun initView() {
        super.initView()
        // 监听加载状态变化 从而改变页面
        mViewModel.loadState.observe(this, Observer { changeState(it) })
        // 监听数据变化,从而显示数据在页面
        mViewModel.myData.observe(this, Observer { showMyData(it) })
        }
        /**
        * 显示数据到页面
        */
     private fun showMyData(data: MyData){
     	nameTv.text = data.name
     }
}

界面布局就不贴了 只有一个TextView。

如有疑问,欢迎留言!

发布了63 篇原创文章 · 获赞 67 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/sange77/article/details/103959389