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

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

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

結合Jetpack,構建快速開發(fā)的MVVM框架。

項目使用Jetpack:LiveData、ViewModel、Lifecycle、Navigation組件。

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

支持快速生成ListActivity、ListFragment;

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

完整文章前往Github瀏覽

前言

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

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

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

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

對基礎框架進行模塊分離, 分為 MVVM Library--MVVM Navigation Library--MVVM Network Library 可基于業(yè)務需求使用 MVVM Library 、MVVM 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 只負責網(wǎng)絡處理
	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ù)中進行配置和初始化相關參數(shù),可以在這里配置網(wǎng)絡請求框架的參數(shù)和UI全局參數(shù)。比如攔截器多域名,全局ActivityFragment屬性。

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

private fun initHttpManager() {
    // 參數(shù)攔截器
    HttpManager.instance.setting {
        // 設置網(wǎng)絡屬性
        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 ->
                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()))
    }
}
// 需要重寫,傳入當前是否初始Debug模式。
override fun isLogDebug(): Boolean {
    // 是否顯示日志
    return BuildConfig.DEBUG

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

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

/**
 * 過濾服務器結果,失敗拋異常
 * @param block 請求體方法,必須要用suspend關鍵字修飾
 * @param success 成功回調
 * @param error 失敗回調 可不傳
 * @param isLoading 是否顯示 Loading 布局
 * @param loadingMessage 加載框提示內容
 */
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)絡請求成功, 結束請求
            httpCallback.afterNetwork.postValue(false)
            //校驗請求結果碼是否正確,不正確會拋出異常走下面的onFailure
            kotlin.runCatching {
                executeResponse(it) { coroutine ->
                    success(coroutine)
                }
            }.onFailure { error ->
                // 請求時發(fā)生異常, 執(zhí)行失敗回調
                val responseThrowable = ExceptionHandle.handleException(error)
                httpCallback.onFailed.value = responseThrowable.errorMsg ?: ""
                responseThrowable.errorLog?.let { errorLog ->
                    LogUtil.e(errorLog)
                }
                // 執(zhí)行失敗的回調方法
                error(responseThrowable)
            }
        }.onFailure { error ->
            // 請求時發(fā)生異常, 執(zhí)行失敗回調
            val responseThrowable = ExceptionHandle.handleException(error)
            httpCallback.onFailed.value = responseThrowable.errorMsg ?: ""
            responseThrowable.errorLog?.let { errorLog ->
                LogUtil.e(errorLog)
            }
            // 執(zhí)行失敗的回調方法
            error(responseThrowable)
        }
    }
}

/**
 * 不過濾服務器結果
 * @param block 請求體方法,必須要用suspend關鍵字修飾
 * @param success 成功回調
 * @param error 失敗回調 可不傳
 * @param isLoading 是否顯示 Loading 布局
 * @param loadingMessage 加載框提示內容
 */
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)絡請求成功, 結束請求
            httpCallback.afterNetwork.postValue(false)
            //成功回調
            success(it)
        }.onFailure { error ->
            // 請求時發(fā)生異常, 執(zhí)行失敗回調
            val responseThrowable = ExceptionHandle.handleException(error)
            httpCallback.onFailed.value = responseThrowable.errorMsg ?: ""
            responseThrowable.errorLog?.let { errorLog ->
                LogUtil.e(errorLog)
            }
            // 執(zhí)行失敗的回調方法
            error(responseThrowable)
        }
    }
}

/**
 * 請求結果過濾,判斷請求服務器請求結果是否成功,不成功則會拋出異常
 */
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)絡請求擴展函數(shù),并且可以根據(jù)自己的情況,選擇脫殼或者不脫殼的回調處理。 調用示例:

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

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

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

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

框架結構

mvvm

該組件對Activity和Fragment進行常用屬性封裝

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

Activity封裝

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

Fragment封裝

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

Adapter封裝

每個項目中肯定會有列表的頁面,所以還需要對Adapter進行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進行封裝,詳細的可以查看KunMinX的**UnPeek-LiveData**。

Navigation封裝

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

ViewModel封裝

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

open class BaseViewModel : ViewModel() {

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

    inner class HttpCallback {

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

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

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

輔助類封裝

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

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

管理類封裝

通過Lifecycle結合AppManager對Activity的進出棧管理。

mvvm_navigation

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

mvvm_network

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

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

相關文章

  • Android view自定義帶文字帶進度的控件

    Android view自定義帶文字帶進度的控件

    這篇文章主要為大家詳細介紹了Android view自定義帶文字帶進度的控件,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Android RecyclerView實現(xiàn)吸頂動態(tài)效果流程分析

    Android RecyclerView實現(xiàn)吸頂動態(tài)效果流程分析

    RecyclerView是Android一個更強大的控件,其不僅可以實現(xiàn)和ListView同樣的效果,還有優(yōu)化了ListView中的各種不足。其可以實現(xiàn)數(shù)據(jù)縱向滾動,也可以實現(xiàn)橫向滾動(ListView做不到橫向滾動)。接下來講解RecyclerView的用法
    2022-12-12
  • Android編程判斷當前應用是否在后臺運行的方法示例

    Android編程判斷當前應用是否在后臺運行的方法示例

    這篇文章主要介紹了Android編程判斷當前應用是否在后臺運行的方法,涉及Android針對當前程序運行狀態(tài)相關屬性操作與判定技巧,需要的朋友可以參考下
    2018-03-03
  • Android系統(tǒng)永不鎖屏永不休眠的方法

    Android系統(tǒng)永不鎖屏永不休眠的方法

    在進行Android系統(tǒng)開發(fā)的時候,有些特定的情況需要設置系統(tǒng)永不鎖屏,永不休眠.本篇文章給大家介紹Android 永不鎖屏,開機不鎖屏,刪除設置中休眠時間選項,需要的朋友一起學習吧
    2016-07-07
  • Android 三種動畫詳解及簡單實例

    Android 三種動畫詳解及簡單實例

    這篇文章主要介紹了Android 三種動畫詳解及簡單實例的相關資料,需要的朋友可以參考下
    2017-04-04
  • android中px和dp,px和sp之間的轉換方法

    android中px和dp,px和sp之間的轉換方法

    在Android開發(fā)中dp和px,sp和px之間的轉換時必不可少的。下面腳本之家小編給大家?guī)砹薬ndroid中px和dp,px和sp之間的轉換方法,感興趣的朋友一起看看吧
    2018-06-06
  • Android實現(xiàn)簡單的加載進度條

    Android實現(xiàn)簡單的加載進度條

    這篇文章主要為大家詳細介紹了Android實現(xiàn)簡單的加載進度條,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • android okhttp的基礎使用【入門推薦】

    android okhttp的基礎使用【入門推薦】

    本文主要總結了Android著名網(wǎng)絡框架-okhttp的基礎使用。具有一定的參考價值,下面跟著小編一起來看下吧
    2017-01-01
  • 在Linux下通過命令行打包Android應用的方法

    在Linux下通過命令行打包Android應用的方法

    這篇文章主要介紹了在Linux下通過命令行打包Android應用的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • Android?RecyclerView實現(xiàn)九宮格效果

    Android?RecyclerView實現(xiàn)九宮格效果

    這篇文章主要為大家詳細介紹了Android?RecyclerView實現(xiàn)九宮格效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06

最新評論