IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android:玩转Jetpack Compose之MVI架构——基类中使用页面UiState -> 正文阅读

[移动开发]Android:玩转Jetpack Compose之MVI架构——基类中使用页面UiState

系列文章目录

架构一(MVP):Android:玩转Retrofit+OkHttp+Kotlin协程 网络请求架构
架构二(MVVM):Android:玩转网络请求架构 Retrofit+Kotlin协程简单使用(MVVM架构模式)
架构三(MVI):Android:玩转Jetpack Compose之MVI架构——基类中使用页面UiState



前言

自去年Google发布了Compose正式版后,就开始将其逐渐应用至项目中,页面编码方式在改变,架构也在变化,也就是现在Google官方建议的MVI

本文不打算再叙述对于架构的理解,官网和其他博主已有许多文章;今天主要讲下我在架构迁移中遇到的一些问题,以及解决方案;

官方应用架构指南: https://developer.android.google.cn/topic/architecture#common-principles
注:本文全部内容均为Kotlin语言。(2202年了,相信伙伴们都早已经掌握了kotlin)

官方推荐架构示意图


一、基类中使用页面UiState

在MVI架构中,每个页面都会有且只有一个UiState数据对象,用以驱动页面UI的更新;
这个UiState对象可以是一个sealed class (密封类),也可以是data class(数据类),当然它也可以是一个class,都可以实现。

我这里是用data class, 在使用中有这么一个场景:对于ViewModel中公共的逻辑,我们会抽出个基类封装,当基类处理业务逻辑的过程中,可能需要更新UI。也就是我们要在viewModel基类中更新UiState对象。

我们一想,这还不好办嘛!把UiState中要修改的属性,抽取出个基类来就可以了。
一顿虎操作后,我们的代码是这样的:

ViewModel基类:BaseMviVM.kt

/**
 * UI状态 基类
 * @param loadState 页面加载状态, 一般默认为 LoadState.Success()
 */
@Suppress("UNCHECKED_CAST")
abstract class BaseMviUiState<T : BaseMviUiState<T>> {
    var loadState: LoadState = LoadState.Success()

    /**
     * 复制对象
     */
    abstract fun copyObject(): T
}

/**
 * ViewModel 基类 (MVI 架构)
 */
abstract class BaseMviVM<T : BaseMviUiState<T>>(uiState: T) : ViewModel() {
    protected val _uiState = MutableStateFlow(uiState)
    val uiState: StateFlow<T> = _uiState.asStateFlow()

    /**
     * 改变页面加载状态
     */
    fun changeLoadState(loadState: LoadState) {
        _uiState.update {
            it.copyObject().apply { this.loadState = loadState }
        }
    }
}

由于data class不能继承data class, 它可以继承 abstract class, 或者interface。所以这里在abstract class中增加了页面加载状态属性,并在ViewModel基类中封装了修改更新加载状态方法;利用标准函数apply,修改类内部属性。 后期可以再增加属性,这样也方便调用。

用户信息页ViewModel:UserInfoScreenVM.kt

data class UserInfoUiState(
    val name: String = "",
    val phone: String = ""
): BaseMviUiState<UserInfoUiState>() {
    override fun copyObject(): UserInfoUiState = this.copy()
}

/**
 * 用户信息页
 */
@HiltViewModel
class UserInfoScreenVM(savedStateHandle: SavedStateHandle): BaseMviVM<UserInfoUiState>(UserInfoUiState()) {
    private val mUserId: String = savedStateHandle.get<String>("key_user_id") ?: ""

    init {
        getUserInfo(mUserId)
    }

    /**
     * 获取用户信息
     * @param userId 用户ID
     */
    private fun getUserInfo(userId: String) = launch({
        // 显示Loading状态UI
        changeLoadState(LoadState.Loading())
        withContext(Dispatchers.IO){
            // 请求网络Api,耗时任务
            // .....
        }
        // 显示成功状态UI
        changeLoadState(LoadState.Success())
    },{
        // 显示失败状态UI
        changeLoadState(LoadState.Fail())
    })
}

在UserInfoUiState中实现了复制对象方法, 这是因为,copy()方法是data class 类特有的方法。

用户信息页面UI:UserInfoScreen.kt

@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
fun UserInfoScreen(
    vm: UserInfoScreenVM = viewModel()
) {
    val uiState by vm.uiState.collectAsStateWithLifecycle()
    // 显示加载中UI
    if(uiState.loadState is LoadState.Loading){
        LoadingView()
    }
}

就这样运行后,打开页面,却没有显示加载Loading控件;断点来看,_uiState 的更新确实走了,但页面组合却没有重组,猜测肯定是_uiState 没有更新成功;

尝试换成更新其他属性

// 在子类更新name, 此句更新成功
_uiState.update {
            it.copyObject(name = "")
        }
        // 去除参数,此句更新失败
_uiState.update {
            it.copyObject()
        }
        // 子类更新加载状态,此句更新失败
_uiState.update {
            it.copy().apply { this.loadState = loadState }
        }
        
        // 子类更新名字不同,加载状态也不同,此句更新成功
_uiState.update {
            it.copy(name = "新名字").apply { this.loadState = loadState }
        }
        
        // 子类更新名字相同,加载状态不同,此句更新失败
_uiState.update {
            it.copy(name = "相同名字").apply { this.loadState = loadState }
        }

现在我们基本上可以发现,更新是否成功取决于 copy()方法形参的变化是否不同,这是因为StateFlow内部做了diff对比操作,就像之前我们写RecyclerView的Adapter 进行Different 对比数据,决定是否更新列表Item 类似。

二、改造UiState基类

如果只通过apply来更新其他属性,就不合适了。 所以我们要把UiState基类中的属性都放进构造方法参数中才可以,让子类override重写 父类的构造方法参数,然后传给父类即可。改造后的代码如下:

/**
 * UI状态 基类
 * @param loadState 页面加载状态, 一般默认为 LoadState.Success()
 */
@Suppress("UNCHECKED_CAST")
abstract class BaseMviUiState<T : BaseUiState<T>>(open val loadState: LoadState) {

    /**
     * 复制对象
     */
    abstract fun copyObject(loadState: LoadState): T
}

/**
 * ViewModel 基类 (MVI 架构)
 * @author ssq
 */
abstract class BaseMviVM<T : BaseUiState<T>>(uiState: T) : ViewModel() {
    protected val _uiState = MutableStateFlow(uiState)
    val uiState: StateFlow<T> = _uiState.asStateFlow()

    /**
     * 改变页面加载状态
     */
    fun changeLoadState(loadState: LoadState) {
        _uiState.update {
            it.copyObject(loadState)
        }
    }
}

基类中主要是构造方法变化,抽象方法变化,调用更新变化。

data class UserInfoUiState(
    override val loadState: LoadState = LoadState.Success(),
    val name: String = "",
    val phone: String = ""
): BaseMviUiState<UserInfoUiState>(loadState) {
    override fun copyObject(loadState: LoadState): UserInfoUiState = this.copy(loadState = loadState)
}

在data class 实现类,重写父类的属性,实现copyObject 传递参数,复制对象即可。


总结

以上就是今天要讲的内容,本文仅仅叙述了MVI 迁移中比较有特点的问题,也还会有其他问题,需要逐渐解决,最后会充分理解MVI架构的思想。

最后的最后…可能…还会有新的架构出现…

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:53:18  更:2022-10-08 20:53:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 2:42:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码