前言
在前一篇中,介绍了 KtArmor—MVVM 简单的使用方法,但是这往往不是全部。
在持续迭代、维护下,发现功能越写越多,也相应复杂起来。
所以后续,我尽可能编写详细 说明文档,并且在源码编写 注释
废话不多说,进入正文。
ViewModel
回到上文所说的 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>
KResponse
由于 quickLaunch DSL 方式默认实现了 Success,Failure 逻辑处理,并且进行相应回调。所以 Retrofit 的 api service 返回的 BaseResponse,必须实现 KResponse 接口。
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 接口,并且实现对应的方法
- isSuccess(必须)
- getKData(必须)
- getKMessage(必须)
- executeRsp
- execute
其中 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- print log。
- hideLoading
- ....
如下代码所示:
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
在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) }
}
/**
* 等同上面 superLaunch
*/
superLaunchRequest(loginData) { repository.login(account, password) }
在原有基础上,MutableLiveData,切换成 CommonLiveData
并且新增一个 superLaunch DSL 方法,简化了 quickLaunch DSL 方法 的赋值操作
当然 你也可以根据自身业务,重写 onSuccess, onFailure, onException 等方法。
然后我们再来看看 Activity,代码上,就更加方便简单明了。
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 提供对应接口,给开发者进行自定义扩展
Activity
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 默认处理逻辑
ViewModel
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 一起交流学习。
如有不妥, 望各位大佬指出。
KtArmor-MVVM 源码传送门
相关文章
MVVM
MVP
下次再见