在前一篇中,介绍了 KtArmor—MVVM 简单的使用方法,但是这往往不是全部。
在持续迭代、维护下,发现功能越写越多,也相应复杂起来。
所以后续,我尽可能编写详细 说明文档,并且在源码编写 注释
废话不多说,进入正文。
回到上文所说的 Login 示例,我们在 LoginViewModel,通过quickLaunch DSL 方式,发起网络请求。如下代码所示:
class LoginViewModel : BaseViewModel<LoginRepository>() { val loginData = MutableLiveData<LoginRsp>() fun login(account: String, password: String) { ...省略其他代码 // DSL 方式发起 网络请求 quickLaunch<LoginRsp> { onStart { showLoading() } request { repository.login(account, password) } onSuccess { loginData.value = it } } } } 复制代码
quickLaunch 方法,需传入范型( 返回值类型),即调用 repository.login(account, password) 返回值类型, 且必须实现 KResponse 接口,如下代码所示:
@POST(API.LOGIN) suspend fun login(@Query("username") username: String, @Query("password") password: String): BaseResponse<LoginRsp> 复制代码
由于 quickLaunch DSL 方式默认实现了 Success,Failure 逻辑处理,并且进行相应回调。所以 Retrofit 的 api service 返回的 BaseResponse,必须实现 KResponse 接口。
data class BaseResponse<T>(var data: T?, var errorCode: Int = -1, var errorMsg: String = "") : KResponse<T> { override fun isSuccess(): Boolean = errorCode == 0 override fun getKData(): T? = data override fun getKMessage(): String? = errorMsg } 复制代码
以上是参考代码,根据后台接口返回的类型,新建一个 基类 Response(BaseResponse),实现 KResponse 接口,并且实现对应的方法
其中 executeRsp,execute 默认实现了 默认的处理逻辑,如下所示
/** * 全局默认实现, 可根据自身业务 重写execute方法 * @param error 若有错误的回调, 默认getKMessage(), 否则返回 Setting.MESSAGE_EMPTY * @param successResponse 成功的回调, 默认是返回 KResponse<T> */ fun executeRsp(successResponse: ((KResponse<T>) -> Unit)?, error: ((String) -> Unit)? = null) { if (this.isSuccess()) { successResponse?.invoke(this) return } (this.getKMessage() ?: Setting.MESSAGE_EMPTY).let { error?.invoke(it) ?: Toasts.show(it) } } /** * 全局默认实现, 可根据自身业务 重写execute方法 * @param success 成功的回调, 默认是返回 getKData() * @param error 若有错误的回调, 默认getKMessage(), 否则返回 Setting.MESSAGE_EMPTY */ fun execute(success: ((T?) -> Unit)?, error: ((String) -> Unit)? = null) { if (this.isSuccess()) { success?.invoke(this.getKData()) return } (this.getKMessage() ?: Setting.MESSAGE_EMPTY).let { error?.invoke(it) ?: Toasts.show(it) } } 复制代码
以 execute 为例,默认是 根据 isSuccess
判断是否成功,并且 回调到 quickLaunch 方法的 onSuccess、onFailure 方法。开发者可以根据自身需求,对 execute/executeRsp 方法重写。
你以为这样就完事了吗?
一般情况下,我们获取了 repository 返回的数据,在 onSuccess 方法处理自己的业务逻辑。
然后 遇到异常,会进行如下操作:
toast
显示 message如下代码所示:
quickLaunch<LoginRsp> { request { repository.login(account, password) } onSuccess { loginSuccessData.value = it } onFailure { loginFailData.value = it } onException { loginExceptionLiveData = it } } 复制代码
由于 Activity 是通过观察 ViewModel 中 liveData 数据变化,进而做 请求成功,请求失败 等逻辑处理。
所以首先想到是 新增 三个 LiveData,来通知 Activity,但是每次请求,都需要如此操作,这时候需要封装一下了。
KtArmor-MVVM, 尝试引入一个 复合 LiveData—— CommonLiveData,解决上面问题, 所谓 复合,本质就是 CommonLiveData,里面包含 两个LiveData,如下代码所示:
class CommonLiveData<T> : MutableLiveData<T>() { val errorLiveData = MutableLiveData<String>() ... 省略其他 } 复制代码
在ViewModel 中 使用方式如下
val loginData = CommonLiveData<LoginRsp>() // 看这里 ^^^^ quickLaunch<LoginRsp> { request { repository.login(account, password) } onSuccess { loginData.value = it } onFailure { loginData.failureMessage = it } onException { loginData.exception = it } } /** * 等同上面 quickLaunch */ superLaunch(loginData) { request { repository.login(account, password) } } 复制代码
在原有基础上,MutableLiveData,切换成 CommonLiveData
并且新增一个 superLaunch DSL 方法,简化了 quickLaunch DSL 方法 的赋值操作
当然 你也可以根据自身业务,重写 onSuccess, onFailure, onException 等方法。
然后我们再来看看 Activity,代码上,就更加方便简单明了。
class LoginActivity : AppCompatActivity(), IMvmActivity { ...省略其他代码 override fun dataObserver() { // 正常 MutableLiveData 监听 viewModel.loginData.observe(this, Observer { toast("登录成功") }) // CommonLiveData 监听 quickObserve(loginViewModel.loginData) { onSuccess { toast("登录成功") } onFailure { message -> toast(message) } onException { throwable -> toast(R.string.unkown_error) logd(msg) } } /** * CommonLiveData 监听, 等同上面, 只监听 Success 情况 * onSuccess: 自定义实现 * onFailure: 默认 toast message (viewModel 传递过来的 message) * onException: 默认 toast "未知异常"(固定), 打印 log */ quickObserveSuccess(loginViewModel.loginData) { toast("登录成功") } } } 复制代码
还是以 Login 示例,我们在 LoginActivity 中,需要 observe, LoginViewModel 中的 loginData,来获取 请求成功后的数据。
若是 使用了 CommonLiveData,则我们只需调用 quickObserve 或 quickObserveSuccess,即可监听到 ViewModel 传递过来的数据,进行相应的处理。
具体方法说明,都在上面代码注释所示。
看到这里,可能有小伙伴会说了,你的默认实现,不符合我的业务,不喜欢,想要修改,怎么办。
没问题!
KtArmor-MVVM 提供对应接口,给开发者进行自定义扩展
class MyActivityActuator : IActivityActuator { override fun <R> success(mvmView: IMvmView, data: R?) { } override fun failure(mvmView: IMvmView, message: String?) { } override fun exception(mvmView: IMvmView, throwable: Throwable?) { } } 复制代码
新建 MyActivityActuator, 并且实现 IActivityActuator 接口,自定义 quickObserve 默认处理逻辑。
ViewModel ,也是同理。实现 ILiveDataActuator 接口,自定义 superLaunch 默认处理逻辑
class MyLiveDataActuator : ILiveDataActuator() { override fun <R> success(liveData: CommonLiveData<R>, data: R?) { } override fun <R> failure(liveData: CommonLiveData<R>, message: String?) { } override fun <R> exception(liveData: CommonLiveData<R>, throwable: Throwable?) { } } 复制代码
最后别忘了,需要 配置到 KtArmor 中,这样才会生效!
class BaseApplication : Application() { override fun onCreate() { super.onCreate() // KtArmor 相关配置 with(KtArmor){ configActivityActuator(MyActivityActuator()) configLiveDataActuator(MyLiveDataActuator()) } } } 复制代码
代码整体实现上,相对简单,遵循开源框架的 三部曲,使得框架 可全局配置,可局部配置。
后续想到更好的方案,在进行优化,
不知你们意向如何? 期待小伙伴们更好方案 : )
KtArmor-MVVM
框架是一款小而美的框架,也是我个人经验的积累, 总结,希望大家喜欢。
如果你有更好的建议欢迎 pr,issues 一起交流学习。
如有不妥, 望各位大佬指出。
下次再见