Kotlin的圣光机甲——KtArmor通用流程篇 (二)

1,139 阅读5分钟

前言

前一篇中,介绍了 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,则我们只需调用 quickObservequickObserveSuccess,即可监听到 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

下次再见