欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

從0快速搭建一個實(shí)用的MVVM框架(超詳細(xì))

 更新時間:2022年03月16日 10:55:26   作者:臭豆腐炒雞蛋  
這篇文章主要介紹了從0搭建一個實(shí)用的MVVM框架,結(jié)合Jetpack,構(gòu)建快速開發(fā)的MVVM框架,支持快速生成ListActivity、ListFragment,主要是基于MVVM進(jìn)行快速開發(fā)上手即用,需要的朋友可以參考下

結(jié)合Jetpack,構(gòu)建快速開發(fā)的MVVM框架。

項(xiàng)目使用Jetpack:LiveData、ViewModel、Lifecycle、Navigation組件。

支持動態(tài)加載多狀態(tài)布局:加載中、成功、失敗、標(biāo)題;

支持快速生成ListActivity、ListFragment;

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

完整文章前往Github瀏覽

前言

隨著GoogleJetpack的完善,對于開發(fā)者來說,MVVM顯得越來越高效與方便。

對于使用MVVM的公司來說,都有一套自己的MVVM框架,但是我發(fā)現(xiàn)有些只是對框架進(jìn)行非常簡單的封裝,導(dǎo)致在開發(fā)過程中會出現(xiàn)很多沒必要的冗余代碼。

這篇文章主要就是分享如何從0搭建一個高效的MVVM框架。

基于MVVM進(jìn)行快速開發(fā), 上手即用。(重構(gòu)已完成,正在編寫SampleApp)

對基礎(chǔ)框架進(jìn)行模塊分離, 分為 MVVM Library--MVVM Navigation Library--MVVM Network Library 可基于業(yè)務(wù)需求使用 MVVM LibraryMVVM Navigation Library、MVVM Network Library

已開發(fā)一鍵生成代碼模板, 創(chuàng)建適用于本框架的Activity和Fragment. 具體查看AlvinMVVMPlugin_4_3

如何集成

To get a Git project into your build:

Step 1. Add the JitPack repository to your build file

Add it in your root build.gradle at the end of repositories:

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

Step 2. Add the dependency

dependencies {
	// MVVM 基類
	implementation 'com.github.Chen-Xi-g.MVVMFramework:mvvm_framework:Tag'
	// MVVM Network 只負(fù)責(zé)網(wǎng)絡(luò)處理
	implementation 'com.github.Chen-Xi-g.MVVMFramework:mvvm_network:Tag'
	// MVVM Navigation 組件抽離
	implementation 'com.github.Chen-Xi-g.MVVMFramework:mvvm_navigation:Tag'
}
說明依賴地址版本號
MVVM 基類implementation 'com.github.Chen-Xi-g.MVVMFramework:mvvm_framework:Tag'MVVM
MVVM Networkimplementation 'com.github.Chen-Xi-g.MVVMFramework:mvvm_network:Tag'MVVM
MVVM Navigationimplementation 'com.github.Chen-Xi-g.MVVMFramework:mvvm_navigation:Tag'MVVM

依賴引入后,需要初始化依賴,下面是模塊化初始化流程。

1.繼承BaseApplication

創(chuàng)建你的Application,繼承BaseApplication,并且需要在onCreate函數(shù)中進(jìn)行配置和初始化相關(guān)參數(shù),可以在這里配置網(wǎng)絡(luò)請求框架的參數(shù)和UI全局參數(shù)。比如攔截器多域名,全局ActivityFragment屬性。

// 全局Activity設(shè)置
GlobalMVVMBuilder.initSetting(BaseActivitySetting(), BaseFragmentSetting())

private fun initHttpManager() {
    // 參數(shù)攔截器
    HttpManager.instance.setting {
        // 設(shè)置網(wǎng)絡(luò)屬性
        setTimeUnit(TimeUnit.SECONDS) // 時間類型 秒, 框架默認(rèn)值 毫秒
        setReadTimeout(30) // 讀取超時 30s, 框架默認(rèn)值 10000L
        setWriteTimeout(30) // 寫入超時 30s, 框架默認(rèn)值 10000L
        setConnectTimeout(30) // 鏈接超時 30s,框架默認(rèn)值 10000L
        setRetryOnConnectionFailure(true) // 超時自動重連, 框架默認(rèn)值 true
        setBaseUrl("https://www.wanandroid.com") // 默認(rèn)域名
        // 多域名配置
        setDomain {
            Constant.domainList.forEach { map ->
                map.forEach {
                    if (it.key.isNotEmpty() && it.value.isNotEmpty()) {
                        put(it.key, it.value)
                    }
                }
            }
        }
        setLoggingInterceptor(
            isDebug = BuildConfig.DEBUG,
            hideVerticalLine = true,
            requestTag = "HTTP Request 請求參數(shù)",
            responseTag = "HTTP Response 返回參數(shù)"
        )
        // 添加攔截器
        setInterceptorList(hashSetOf(ResponseInterceptor(), ParameterInterceptor()))
    }
}
// 需要重寫,傳入當(dāng)前是否初始Debug模式。
override fun isLogDebug(): Boolean {
    // 是否顯示日志
    return BuildConfig.DEBUG

2.創(chuàng)建ViewModel擴(kuò)展函數(shù)

所有模塊需要依賴的base模塊創(chuàng)建ViewModel相關(guān)的擴(kuò)展函數(shù)VMKxt和Json實(shí)體類殼BaseEntity。

/**
 * 過濾服務(wù)器結(jié)果,失敗拋異常
 * @param block 請求體方法,必須要用suspend關(guān)鍵字修飾
 * @param success 成功回調(diào)
 * @param error 失敗回調(diào) 可不傳
 * @param isLoading 是否顯示 Loading 布局
 * @param loadingMessage 加載框提示內(nèi)容
 */
fun <T> BaseViewModel.request(
    block: suspend () -> BaseResponse<T>,
    success: (T?) -> Unit,
    error: (ResponseThrowable) -> Unit = {},
    isLoading: Boolean = false,
    loadingMessage: String? = null
): Job {
    // 開始執(zhí)行請求
    httpCallback.beforeNetwork.postValue(
        // 執(zhí)行Loading邏輯
        LoadingEntity(
            isLoading,
            loadingMessage?.isNotEmpty() == true,
            loadingMessage ?: ""
        )
    )
    return viewModelScope.launch {
        kotlin.runCatching {
            //請求體
            block()
        }.onSuccess {
            // 網(wǎng)絡(luò)請求成功, 結(jié)束請求
            httpCallback.afterNetwork.postValue(false)
            //校驗(yàn)請求結(jié)果碼是否正確,不正確會拋出異常走下面的onFailure
            kotlin.runCatching {
                executeResponse(it) { coroutine ->
                    success(coroutine)
                }
            }.onFailure { error ->
                // 請求時發(fā)生異常, 執(zhí)行失敗回調(diào)
                val responseThrowable = ExceptionHandle.handleException(error)
                httpCallback.onFailed.value = responseThrowable.errorMsg ?: ""
                responseThrowable.errorLog?.let { errorLog ->
                    LogUtil.e(errorLog)
                }
                // 執(zhí)行失敗的回調(diào)方法
                error(responseThrowable)
            }
        }.onFailure { error ->
            // 請求時發(fā)生異常, 執(zhí)行失敗回調(diào)
            val responseThrowable = ExceptionHandle.handleException(error)
            httpCallback.onFailed.value = responseThrowable.errorMsg ?: ""
            responseThrowable.errorLog?.let { errorLog ->
                LogUtil.e(errorLog)
            }
            // 執(zhí)行失敗的回調(diào)方法
            error(responseThrowable)
        }
    }
}

/**
 * 不過濾服務(wù)器結(jié)果
 * @param block 請求體方法,必須要用suspend關(guān)鍵字修飾
 * @param success 成功回調(diào)
 * @param error 失敗回調(diào) 可不傳
 * @param isLoading 是否顯示 Loading 布局
 * @param loadingMessage 加載框提示內(nèi)容
 */
fun <T> BaseViewModel.requestNoCheck(
    block: suspend () -> T,
    success: (T) -> Unit,
    error: (ResponseThrowable) -> Unit = {},
    isLoading: Boolean = false,
    loadingMessage: String? = null
): Job {
    // 開始執(zhí)行請求
    httpCallback.beforeNetwork.postValue(
        // 執(zhí)行Loading邏輯
        LoadingEntity(
            isLoading,
            loadingMessage?.isNotEmpty() == true,
            loadingMessage ?: ""
        )
    )
    return viewModelScope.launch {
        runCatching {
            //請求體
            block()
        }.onSuccess {
            // 網(wǎng)絡(luò)請求成功, 結(jié)束請求
            httpCallback.afterNetwork.postValue(false)
            //成功回調(diào)
            success(it)
        }.onFailure { error ->
            // 請求時發(fā)生異常, 執(zhí)行失敗回調(diào)
            val responseThrowable = ExceptionHandle.handleException(error)
            httpCallback.onFailed.value = responseThrowable.errorMsg ?: ""
            responseThrowable.errorLog?.let { errorLog ->
                LogUtil.e(errorLog)
            }
            // 執(zhí)行失敗的回調(diào)方法
            error(responseThrowable)
        }
    }
}

/**
 * 請求結(jié)果過濾,判斷請求服務(wù)器請求結(jié)果是否成功,不成功則會拋出異常
 */
suspend fun <T> executeResponse(
    response: BaseResponse<T>,
    success: suspend CoroutineScope.(T?) -> Unit
) {
    coroutineScope {
        when {
            response.isSuccess() -> {
                success(response.getResponseData())
            }
            else -> {
                throw ResponseThrowable(
                    response.getResponseCode(),
                    response.getResponseMessage(),
                    response.getResponseMessage()
                )
            }
        }
    }
}

以上代碼封裝了快速的網(wǎng)絡(luò)請求擴(kuò)展函數(shù),并且可以根據(jù)自己的情況,選擇脫殼或者不脫殼的回調(diào)處理。 調(diào)用示例:

/**
 * 加載列表數(shù)據(jù)
 */
fun getArticleListData(page: Int, pageSize: Int) {
    request(
        {
            filterArticleList(page, pageSize)
        }, {
            // 成功操作
            it?.let {
                _articleListData.postValue(it.datas)
            }
        }
    )
}

完成上面的操作,你就可以進(jìn)入愉快的開發(fā)工作了。

3.引入一鍵生成代碼插件(可選)

每次創(chuàng)建Activity、Fragment、ListActivity、ListFragment都是重復(fù)的工作,為了可以更高效的開發(fā),減少這些枯燥的操作,特地編寫的快速生成MVVM代碼的插件,該插件只適用于當(dāng)前MVVM框架,具體使用請前往AlvinMVVMPlugin。集成后你就可以開始像創(chuàng)建EmptyActivity這樣創(chuàng)建MVVMActivity。

框架結(jié)構(gòu)

mvvm

該組件對Activity和Fragment進(jìn)行常用屬性封裝

  • base包下封裝了MVVM的基礎(chǔ)組件。
    • activity實(shí)現(xiàn)DataBinding + ViewModel的封裝,以及一些其他功能。
    • adapter實(shí)現(xiàn)DataBinding + Adapter的封裝。
    • fragment實(shí)現(xiàn)DataBinding + ViewModel的封裝,以及一些其他功能。
    • livedata實(shí)現(xiàn)LiveData的基礎(chǔ)功能封裝,如基本數(shù)據(jù)類型的非空返回值。
    • view_model實(shí)現(xiàn)BaseViewModel的處理。
  • help包下封裝了組件的輔助類,在BaseApplication中進(jìn)行全局Actiivty、Fragment屬性賦值。
  • manager包下封裝了對Activity的管理。
  • utils包下封裝了LogUtil工具類,通過BaseApplication進(jìn)行初始化。

Activity封裝

  • AbstractActivityActivity的抽象基類,這個類里面的方法適用于全部Activity的需求。 該類中封裝了所有Activity必須實(shí)現(xiàn)的抽象方法。
  • BaseActivity封裝了基礎(chǔ)的Activity功能,主要用來初始化Activity公共功能:DataBinding的初始化、沉浸式狀態(tài)欄、AbstractActivity抽象方法的調(diào)用、屏幕適配、空白區(qū)域隱藏軟鍵盤。具體功能可以自行新增。
  • BaseDialogActivity只負(fù)責(zé)顯示Dialog Loading彈窗,一般在提交請求或本地流處理時使用。也可以擴(kuò)展其他的Dialog,比如時間選擇器之類。
  • BaseContentViewActivity是對布局進(jìn)行初始化操作的Activity,他是我們的核心。這里處理了每個Activity的每個狀態(tài)的布局,一般情況下有:
    • TitleLayout 公共標(biāo)題
    • ContentLayout 主要的內(nèi)容布局,使我們需要程序內(nèi)容的主要容器。
    • ErrorLayout 當(dāng)網(wǎng)絡(luò)請求發(fā)生錯誤,需要對用戶進(jìn)行友好的提示。
    • LoadingLayout 正在加載數(shù)據(jù)的布局,給用戶一個良好的體驗(yàn),避免首次進(jìn)入頁面顯示的布局沒有數(shù)據(jù)。
  • BaseVMActivity實(shí)現(xiàn)ViewModeActivity基類,通過泛型對ViewModel進(jìn)行實(shí)例化。并且通過BaseViewModel進(jìn)行公共操作。
  • BaseMVVMActivity 所有Activity最終需要繼承的MVVM類,通過傳入DataBindingViewModel的泛型進(jìn)行初始化操作,在構(gòu)造參數(shù)中還需要獲取Layout布局
  • BaseListActivity適用于列表的Activity,分頁操作、上拉加載、下拉刷新、空布局、頭布局、底布局封裝。

Fragment封裝

根據(jù)你的需要進(jìn)行不同的封裝,我比較傾向于和Activity具有相同功能的封裝,也就是Activity封裝的功能我Fragment也要有。這樣在使用Navigation的時候可以減少ActivityFragment的差異。這里直接參考Activity的封裝

Adapter封裝

每個項(xiàng)目中肯定會有列表的頁面,所以還需要對Adapter進(jìn)行DataBinding適配,這里使用的Adapter是BRVAH。

abstract class BaseBindingListAdapter<T, DB : ViewDataBinding>(
    @LayoutRes private val layoutResId: Int
) : BaseQuickAdapter<T, BaseViewHolder>(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在使用的時候會出現(xiàn)數(shù)據(jù)倒灌的情況,用簡單的話來描述數(shù)據(jù)倒灌:A訂閱1月1日新聞信息,B訂閱1月15日新聞信息,但是B在1月15日同時收到了1月1日的信息,這明顯不符合我們生活中的邏輯,所以需要對LiveData進(jìn)行封裝,詳細(xì)的可以查看KunMinX的**UnPeek-LiveData**。

Navigation封裝

通過重寫 FragmentNavigator 將原來的 FragmentTransaction.replace() 方法替換為 hide()/Show()

ViewModel封裝

BaseViewModel中封裝一個網(wǎng)絡(luò)請求需要用的LiveData,下面是一個簡單的示例

open class BaseViewModel : ViewModel() {

    // 默認(rèn)的網(wǎng)絡(luò)請求LiveData
    val httpCallback: HttpCallback by lazy { HttpCallback() }

    inner class HttpCallback {

        /**
         * 請求發(fā)生錯誤
         *
         * String = 網(wǎng)絡(luò)請求異常
         */
        val onFailed by lazy { StringLiveData() }

        /**
         * 請求開始
         *
         * LoadingEntity 顯示loading的實(shí)體類
         */
        val beforeNetwork by lazy { EventLiveData<LoadingEntity>() }

        /**
         * 請求結(jié)束后框架自動對 loading 進(jìn)行處理
         *
         * false 關(guān)閉 loading or Dialog
         * true 不關(guān)閉 loading or Dialog
         */
        val afterNetwork by lazy { BooleanLiveData() }
    }
}

輔助類封裝

大部分的ActivityFragment樣式基本相同,比如布局中的TitleLayout、LoadingLayout這些都是統(tǒng)一樣式。所以可以封裝全局的輔助類來對Activity中的屬性進(jìn)行抽離。

  • 定義接口ISettingBaseActivity添加抽離的方法,并且賦于默認(rèn)值。
  • 定義接口ISettingBaseFragment添加抽離的方法,并且賦于默認(rèn)值。
  • 創(chuàng)建ISettingBaseActivityISettingBaseFragment的實(shí)現(xiàn)類,進(jìn)行默認(rèn)的自定義操作。
  • 創(chuàng)建GlobalMVVMBuilder進(jìn)行賦值

管理類封裝

通過Lifecycle結(jié)合AppManager對Activity的進(jìn)出棧管理。

mvvm_navigation

分離Navigation,通過重寫 FragmentNavigator 將原來的 FragmentTransaction.replace() 方法替換為 hide()/Show()。

mvvm_network

使用 Retrofit + OkHttp + Moshi 對網(wǎng)絡(luò)請求進(jìn)行封裝,使用密封類自定義異常處理。

到此這篇關(guān)于從0搭建一個實(shí)用的MVVM框架的文章就介紹到這了,更多相關(guān)MVVM框架搭建內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論