运维开发网

从0快速构建实用的MVVM框架

运维开发网 https://www.qedev.com 2022-07-09 21:23 出处:网络
这篇文章主要介绍了从0搭建一个实用的MVVM框架,结合Jetpack,构建快速开发的MVVM框架,支持快速生成ListActivity、ListFragment,主要是基于MVVM进行快速开发上手即用

这篇文章主要介绍了从0搭建一个实用的MVVM框架,结合Jetpack,构建快速开发的MVVM框架,支持快速生成ListActivity、ListFragment,主要是基于MVVM进行快速开发上手即用

结合Jetpack,构建一个快速开发的MVVM框架。

使用项目Jetpack:LiveData、ViewModel、Lifecycle、Navigation组件。

支持多状态布局的动态加载:加载、成功、失败、标题;

支持快速生成ListActivity和ListFragment;

支持插件快速生成适用于本框架的Activity、Fragment、ListActivity和ListFragment。

全文去Github浏览。


前言

随着Google对Jetpack的改进,MVVM对开发者来说变得越来越高效和方便。

对于使用MVVM的公司来说,他们都有自己的MVVM框架,但我发现他们中的一些只是非常简单地封装了框架,导致开发过程中出现了大量不必要的冗余代码。

这篇文章主要是分享如何从0构建一个高效的MVVM框架。


基于MVVM进行快速开发, 上手即用。(重构已完成,正在编写SampleApp)

基本框架分为MVVM图书馆-MVVM导航图书馆-MVVM网络图书馆。根据业务需要可以使用MVVM图书馆、MVVM导航图书馆和MVVM网络图书馆。

已经开发了一键式代码模板来创建适合该框架的活动和片段。详见AlvinMVVMPlugin_4_3。


如何集成

要在您的构建中加入Git项目:

第一步。将JitPack存储库添加到您的构建文件中

将它添加到您的根build.gradle中,位于repositories的末尾:

allprojects {repositories {...maven { url 'https://jitpack.io' }}}

第二步。添加依赖关系

dependencies {// MVVM 基类implementation 'com.github.Chen-Xi-g.MVVMFramework:mvvm_framework:Tag'// MVVM Network 只负责网络处理implementation 'com.github.Chen-Xi-g.MVVMFramework:mvvm_network:Tag'// MVVM Navigation 组件抽离implementation 'com.github.Chen-Xi-g.MVVMFramework:mvvm_navigation:Tag'}说明依赖地址版本号MVVM 基类implementation #39;com.github.Chen-Xi-g.MVVMFramework:mvvm_framework:Tag#39;MVVM Networkimplementation #39;com.github.Chen-Xi-g.MVVMFramework:mvvm_network:Tag#39;MVVM Navigationimplementation #39;com.github.Chen-Xi-g.MVVMFramework:mvvm_navigation:Tag#39;

引入依赖关系后,需要对其进行初始化。以下是模块化初始化过程。


1.继承BaseApplication

创建你的Application类,继承BaseApplication,需要在onCreate函数中配置初始化相关参数。您可以在此配置网络请求框架的参数和UI全局参数。例如拦截器和多域、全局活动和片段属性。

// 全局Activity设置GlobalMVVMBuilder.initSetting(BaseActivitySetting(), BaseFragmentSetting())private fun initHttpManager() { // 参数拦截器 HttpManager.instance.setting { // 设置网络属性 setTimeUnit(TimeUnit.SECONDS) // 时间类型 秒, 框架默认值 毫秒 setReadTimeout(30) // 读取超时 30s, 框架默认值 10000L setWriteTimeout(30) // 写入超时 30s, 框架默认值 10000L setConnectTimeout(30) // 链接超时 30s,框架默认值 10000L setRetryOnConnectionFailure(true) // 超时自动重连, 框架默认值 true setBaseUrl("https://www.wanandroid.com") // 默认域名 // 多域名配置 setDomain { Constant.domainList.forEach { map -gt; map.forEach { if (it.key.isNotEmpty() amp;amp; it.value.isNotEmpty()) { put(it.key, it.value) } } } } setLoggingInterceptor( isDebug = BuildConfig.DEBUG, hideVerticalLine = true, requestTag = "HTTP Request 请求参数", responseTag = "HTTP Response 返回参数" ) // 添加拦截器 setInterceptorList(hashSetOf(ResponseInterceptor(), ParameterInterceptor())) }}// 需要重写,传入当前是否初始Debug模式。override fun isLogDebug(): Boolean { // 是否显示日志 return BuildConfig.DEBUG


2.创建ViewModel扩展函数

模块需要依赖的所有基础模块都创建了与ViewModel相关的扩展函数VMKxt和Json实体shell BaseEntity。

/** * 过滤服务器结果,失败抛异常 * @param block 请求体方法,必须要用suspend关键字修饰 * @param success 成功回调 * @param error 失败回调 可不传 * @param isLoading 是否显示 Loading 布局 * @param loadingMessage 加载框提示内容 */fun lt;Tgt; BaseViewModel.request( block: suspend () -gt; BaseResponselt;Tgt;, success: (T) -gt; Unit, error: (ResponseThrowable) -gt; Unit = {}, isLoading: Boolean = false, loadingMessage: String = null): Job { // 开始执行请求 httpCallback.beforeNetwork.postValue( // 执行Loading逻辑 LoadingEntity( isLoading, loadingMessage.isNotEmpty() == true, loadingMessage : "" ) ) return viewModelScope.launch { kotlin.runCatching { //请求体 block() }.onSuccess { // 网络请求成功, 结束请求 httpCallback.afterNetwork.postValue(false) //校验请求结果码是否正确,不正确会抛出异常走下面的onFailure kotlin.runCatching { executeResponse(it) { coroutine -gt; success(coroutine) } }.onFailure { error -gt; // 请求时发生异常, 执行失败回调 val responseThrowable = ExceptionHandle.handleException(error) httpCallback.onFailed.value = responseThrowable.errorMsg : "" responseThrowable.errorLog.let { errorLog -gt; LogUtil.e(errorLog) } // 执行失败的回调方法 error(responseThrowable) } }.onFailure { error -gt; // 请求时发生异常, 执行失败回调 val responseThrowable = ExceptionHandle.handleException(error) httpCallback.onFailed.value = responseThrowable.errorMsg : "" responseThrowable.errorLog.let { errorLog -gt; LogUtil.e(errorLog) } // 执行失败的回调方法 error(responseThrowable) } }}/** * 不过滤服务器结果 * @param block 请求体方法,必须要用suspend关键字修饰 * @param success 成功回调 * @param error 失败回调 可不传 * @param isLoading 是否显示 Loading 布局 * @param loadingMessage 加载框提示内容 */fun lt;Tgt; BaseViewModel.requestNoCheck( block: suspend () -gt; T, success: (T) -gt; Unit, error: (ResponseThrowable) -gt; Unit = {}, isLoading: Boolean = false, loadingMessage: String = null): Job { // 开始执行请求 httpCallback.beforeNetwork.postValue( // 执行Loading逻辑 LoadingEntity( isLoading, loadingMessage.isNotEmpty() == true, loadingMessage : "" ) ) return viewModelScope.launch { runCatching { //请求体 block() }.onSuccess { // 网络请求成功, 结束请求 httpCallback.afterNetwork.postValue(false) //成功回调 success(it) }.onFailure { error -gt; // 请求时发生异常, 执行失败回调 val responseThrowable = ExceptionHandle.handleException(error) httpCallback.onFailed.value = responseThrowable.errorMsg : "" responseThrowable.errorLog.let { errorLog -gt; LogUtil.e(errorLog) } // 执行失败的回调方法 error(responseThrowable) } }}/** * 请求结果过滤,判断请求服务器请求结果是否成功,不成功则会抛出异常 */suspend fun lt;Tgt; executeResponse( response: BaseResponselt;Tgt;, success: suspend CoroutineScope.(T) -gt; Unit) { coroutineScope { when { response.isSuccess() -gt; { success(response.getResponseData()) } else -gt; { throw ResponseThrowable( response.getResponseCode(), response.getResponseMessage(), response.getResponseMessage() ) } } }}

上面的代码封装了快速网络请求扩展函数,你可以根据自己的情况选择带壳或者不带壳的回调处理。呼叫示例:

/** * 加载列表数据 */fun getArticleListData(page: Int, pageSize: Int) { request( { filterArticleList(page, pageSize) }, { // 成功操作 it.let { _articleListData.postValue(it.datas) } } )}

完成以上操作,就可以进入愉快的开发工作了。


3.引入一键生成代码插件(可选)

每次创建Activity、Fragment、ListActivity、ListFragment都是一个重复的工作。为了更高效地开发它们,减少这些枯燥的操作,专门编写了一个快速生成MVVM代码的插件,只适用于目前的MVVM框架。具体使用请上AlvinMVVMPlugin。集成之后,就可以像创建EmptyActivity一样开始创建MVVMActivity了。


框架结构


mvvm

该组件封装了活动和片段的公共属性。

base包下封装了MVVM的基础组件。activity实现DataBinding + ViewModel的封装,以及一些其他功能。adapter实现DataBinding + Adapter的封装。fragment实现DataBinding + ViewModel的封装,以及一些其他功能。livedata实现LiveData的基础功能封装,如基本数据类型的非空返回值。view_model实现BaseViewModel的处理。help包下封装了组件的辅助类,在BaseApplication中进行全局Actiivty、Fragment属性赋值。manager包下封装了对Activity的管理。utils包下封装了LogUtil工具类,通过BaseApplication进行初始化。


Activity封装AbstractActivity是Activity的抽象基类,这个类里面的方法适用于全部Activity的需求。 该类中封装了所有Activity必须实现的抽象方法。BaseActivity封装了基础的Activity功能,主要用来初始化Activity公共功能:DataBinding的初始化、沉浸式状态栏、AbstractActivity抽象方法的调用、屏幕适配、空白区域隐藏软键盘。具体功能可以自行新增。BaseDialogActivity只负责显示Dialog Loading弹窗,一般在提交请求或本地流处理时使用。也可以扩展其他的Dialog,比如时间选择器之类。BaseContentViewActivity是对布局进行初始化操作的Activity,他是我们的核心。这里处理了每个Activity的每个状态的布局,一般情况下有:TitleLayout 公共标题ContentLayout 主要的内容布局,使我们需要程序内容的主要容器。ErrorLayout 当网络请求发生错误,需要对用户进行友好的提示。LoadingLayout 正在加载数据的布局,给用户一个良好的体验,避免首次进入页面显示的布局没有数据。BaseVMActivity实现ViewMode的Activity基类,通过泛型对ViewModel进行实例化。并且通过BaseViewModel进行公共操作。BaseMVVMActivity 所有Activity最终需要继承的MVVM类,通过传入DataBinding和ViewModel的泛型进行初始化操作,在构造参数中还需要获取Layout布局BaseListActivity适用于列表的Activity,分页操作、上拉加载、下拉刷新、空布局、头布局、底布局封装。


Fragment封装

根据你的需求,我更喜欢与Activity功能相同的封装,也就是我也想要Fragment中Activity封装的功能。这可以减少使用导航时活动和片段之间的差异。这里直接指活动的封装。


Adapter封装

每个项目中都必须有一个页面列表,因此有必要使适配器适应数据绑定。这里使用的适配器是BRVAH。

abstract class BaseBindingListAdapterlt;T, DB : ViewDataBindinggt;( @LayoutRes private val layoutResId: Int) : BaseQuickAdapterlt;T, BaseViewHoldergt;(layoutResId) { abstract fun convert(holder: BaseViewHolder, item: T, dataBinding: DB) override fun convert(holder: BaseViewHolder, item: T) { convert(holder, item, DataBindingUtil.bind(holder.itemView)) }}


LiveData封装

使用LiveData时,会有数据回流。用简单的话来描述数据回流:A订阅1月1日的新闻信息,B订阅1月15日的新闻信息,但是B在1月15日同时收到1月1日的信息,这显然不符合我们生活的逻辑,所以需要对LiveData进行打包。详情请查看KunMinX的**UnPeek-LiveData**。


Navigation封装

重写FragmentNavigator,用hide()/Show()替换原来的FragmentTransaction.replace()方法


ViewModel封装

将网络请求所需的LiveData封装在BaseViewModel中。这里有一个简单的例子。

open class BaseViewModel : ViewModel() { // 默认的网络请求LiveData val httpCallback: HttpCallback by lazy { HttpCallback() } inner class HttpCallback { /** * 请求发生错误 * * String = 网络请求异常 */ val onFailed by lazy { StringLiveData() } /** * 请求开始 * * LoadingEntity 显示loading的实体类 */ val beforeNetwork by lazy { EventLiveDatalt;LoadingEntitygt;() } /** * 请求结束后框架自动对 loading 进行处理 * * false 关闭 loading or Dialog * true 不关闭 loading or Dialog */ val afterNetwork by lazy { BooleanLiveData() } }}


辅助类封装

大部分活动和片段风格基本一致,比如布局中的TitleLayout和LoadingLayout,都是统一的风格。因此,可以封装全局辅助类来提取活动中的属性。

定义接口ISettingBaseActivity添加抽离的方法,并且赋于默认值。定义接口ISettingBaseFragment添加抽离的方法,并且赋于默认值。创建ISettingBaseActivity和ISettingBaseFragment的实现类,进行默认的自定义操作。创建GlobalMVVMBuilder进行赋值


管理类封装

通过与AppManager相结合的生命周期来管理堆栈内外的活动。


mvvm_navigation

分离导航,通过重写FragmentNavigator,用hide()/Show()替换原来的FragmentTransaction.replace()方法。


mvvm_network

使用retreact+ok http+Moshi封装网络请求,使用密封类自定义异常处理。

这就是这篇关于从0 . 0构建一个实用的MVVM框架的文章。更多MVVM框架建设相关内容

0

精彩评论

暂无评论...
验证码 换一张
取 消