当前位置:   article > 正文

Android Jetpack MVVM封装及使用

android jetpack mvvm

MVVM架构图

架构图
这张图清晰地展示了MVVM的三个模块:Activity / FragmentView层,ViewModel + LiveDataViewModel层,Repository(管理本地和远端数据)为Model层。

封装

理清了MVVM的层次,接下来尝试对其进行简单的封装。

1.BaseRepository

提取了生成Service类的方法apiService供子类使用。
提取了请求接口方法request,出现异常情况则throw,让ViewModel层处理。

abstract class BaseRepository {
    protected fun <T> apiService(tClass: Class<T>): T {
        return ApiClient.createService(tClass)
    }

	@Throws(Exception::class)
    protected suspend fun <T> request(block: suspend () -> BaseData<T>): T {
        val baseData = block()
        if (baseData.errorCode == 0) {
            if (baseData.data == null) {
                throw Exception("baseData.data is null!")
            }
            return baseData.data
        } else {
            throw ApiException(baseData.errorCode, baseData.errorMsg)
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2.BaseViewModel

提取了launch方法,可以一次调用多个接口;处理Model层抛出的异常。

abstract class BaseViewModel : ViewModel() {
    private val _dataLoading = MutableLiveData<Boolean>()
    val dataLoading: LiveData<Boolean> = _dataLoading

    protected fun launch(
        block: suspend () -> Unit,
        error: suspend (Int) -> Unit = {

        }
    ) {
        viewModelScope.launch {
            _dataLoading.value = true

            try {
                block.invoke()
            } catch (e: Exception) {
                when (e) {
                    is ApiException -> {
                        // TODO: use Toast to show errorMsg
                        error(e.code)
                    }
                    is ConnectException, is UnknownHostException, is SocketTimeoutException -> {
                        // TODO: use Toast to show exception message
                        Log.e("Exception", e.localizedMessage)
                    }
                }
            } finally {
                _dataLoading.value = false
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

3.BaseVmActivity

提取viewModel字段及创建方法;添加viewModeldataLoading字段的监听,实现统一的Loading管理。

abstract class BaseVmActivity<VDB : ViewDataBinding, VM : BaseViewModel> : BaseActivity<VDB>() {
    protected val viewModel by lazy { ViewModelProvider(this).get(getVmClass()) }

    private val loadingDialog by lazy { ContentLoadingDialog(this) }

    abstract fun getVmClass(): Class<VM>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        addObserver()
    }

    open fun addObserver() {
        viewModel.dataLoading.observe(this) {
            if (it) {
                loadingDialog.showDialog()
            } else {
                loadingDialog.hideDialog()
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

使用

1.HomeRepository

添加hotkeysbanners方法,请求 玩Android 开放API 的搜索热词及首页banner接口。

class HomeRepository : BaseRepository() {
    suspend fun hotkeys(): List<HotkeyModel> {
        return request {
            apiService(HomeService::class.java).hotkeys()
        }
    }

    suspend fun banners(): List<Any> {
        return request {
            apiService(HomeService::class.java).banners()
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.HomeViewModel

调用HomeRepositoryhotkeysbanners方法(未处理banners接口返回数据,这里只做多个接口调用的演示),返回搜索热词列表,通过DataBinding自动将处理后的hotkeyText显示到TextView上。

class HomeViewModel : BaseViewModel() {
    private val repository by lazy { HomeRepository() }

    private val hotkeyList = MutableLiveData<List<HotkeyModel>>()

    private var _hotkeyText: LiveData<String> = hotkeyList.distinctUntilChanged().map { createHotkeyText(it) }
    val hotkeyText: LiveData<String> = _hotkeyText

    fun hotkeys() {
        launch({
            hotkeyList.value = repository.hotkeys()
            repository.banners()
        })
    }

    private fun createHotkeyText(list: List<HotkeyModel>): String {
        val text = StringBuilder()
        for (key in list) {
            text.append("${key.name}\n")
        }
        return text.toString()
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

3.MvvmActivity

利用DataBindingviewModel赋值给viewmodel,实现数据绑定。

class MvvmActivity : BaseVmActivity<ActivityMvvmBinding, HomeViewModel>() {
    override fun getBinding(): ActivityMvvmBinding {
        return ActivityMvvmBinding.inflate(layoutInflater).apply {
            viewmodel = viewModel
            lifecycleOwner = this@MvvmActivity
        }
    }

    override fun getVmClass(): Class<HomeViewModel> {
        return HomeViewModel::class.java
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

效果图

接口请求太快了,loading都没有来得及显示。。。

demo

demo

上面只展示了MVVMActivity中的使用,demo里也有在Fragment中使用的代码。如需参考完整代码,请移步Github仓库:MVVM Demo

参考

【1】Android Jetpack系列之MVVM使用及封装

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/254076
推荐阅读
相关标签
  

闽ICP备14008679号