Android开发Compose remember原理解析

目录
  • 正文
  • 随机色文本
    • 原因分析
    • 正确实现
  • remember的原理剖析
  • 小结

正文

看过Compose案例或者源码的你,相信肯定是见过 remember 了的。顾名思义,Compose是要让我们的代码“记住”东西,那到底是记住什么呢?要是不 remember,相关功能就实现不了了吗?

带着这些问题,来一探究竟吧

随机色文本

假设有这么一个“随机底色文本”的需求:实现一个 Text,其背景色每次启动都随机产生,且生命周期内不变

用Compose可以实现如下:

private val items = arrayOf(Color.Red, Color.Gray, Color.Magenta, Color.Blue, Color.Green, Color.Cyan)
@Composable
fun ColorText(name: String) {
    val color = items.random()
    val clicked = mutableStateOf(0)
    Log.d("ct", "ui compose")
    Column {
        Text(
            text = "I'm colored: ${clicked.value}", modifier = Modifier
                .padding(16.dp)
                .background(color)
                .clickable {
                    Log.d("ct", "clicked")
                    clicked.value = clicked.value + 1
                }
        )
    }
}

Text 调用 Modifier.background,设置随机从items中取的颜色,每次新的启动,都可能不一样

然而很遗憾,上面的背景色虽然是随机产生,但是单次生命周期里,就可能发生变化 —— 比如点击一下文本,如下图:

更奇怪的是,改变的 clicked 值,并没有如预期一样生效,一直是0……

现象和代码不一?

原因分析

首先,上述代码中的“点击计数” clicked,仅仅是为了测试而存在。因为它是一个 MutableState 对象,点击后改变其值,会触发Recomposition流程,于是组件刷新。这样一来,color 的值将重新由随机函数算出,我们就看到背景色在变化了

同理,虽然我们好像在点击的时候改变了 clicked 的值,希望像view系统一样,界面直接响应响应。但是,因为Recomposition的存在,它又被重新构造了,所以其值还是0

正确实现

要实现背景色的整个生命周期固定,但点击文本后,点击计数要更新,应该这么做:

@Composable
fun ColorText(name: String) {
    val color = remember { items.random() }
    val clicked = remember { mutableStateOf(0) }
    //...
}

仅仅将color和clicked由 remember 包裹起来就解决了问题

remember的原理剖析

前面功能的实现,全仗着remember的加持。它究竟是个啥?

我们先从颜色的remember着手,它调用的是这个:

@Composable
inline fun <T> remember(calculation: @DisallowComposableCalls () -> T): T =
    currentComposer.cache(false, calculation)

其注释写明了两个关键点,这也是它的功能描述:

  • 记忆由calculation返回的值,仅在composition中执行
  • 在Recomposition过程中,不会重新计算,而是直接返回第1步的值

那么这又是怎么做到的呢?

进一步的相关代码:

@ComposeCompilerApi
inline fun <T> Composer.cache(invalid: Boolean, block: () -> T): T {
    @Suppress("UNCHECKED_CAST")
    return rememberedValue().let {
        // 无效或Empty值时,走if流程,计算并保存值,否则直接返回
        // remember传入的invalid为false,所以肯定走值判断
        if (invalid || it === Composer.Empty) {
            val value = block()
            updateRememberedValue(value)
            value
        } else it
    } as T
}
// 要么返回Composer.Empty ,要么返回传给updateRememberedValue的值
@ComposeCompilerApi
fun rememberedValue(): Any?
// 更新调用rememberedValue()后的值,且此值在下一次调用rememberedValue()时返回
@ComposeCompilerApi
fun updateRememberedValue(value: Any?)
interface Composer {
    // ....
    companion object {
        /**
         * 用于标记无值的状态
         */
        val Empty = object {
            override fun toString() = "Empty"
        }
    }
}

从上述代码注释中,基本上已经对原理很清楚了,简单地说就是:

  • 由composer作为存储控制
  • 无值时,走初始化逻辑并返回值,同时存储该值;有值时,直接返回已存储的值

颜色的“值不变”清楚了,那点击计数的“值变”又是怎么回事呢?

其实如出一辙,只是点击计数remember的,不是普通值,而是一个 MutableState 类型。这样一来,它就有两层含义了:

  • MutableState对象本身在整个composition生命周期不变 —— 即类似普通值的状态一致性
  • MutableState对象所存储的实际值,可变 —— 这用以触发Recomposition,并且获取更新值

小结

remember 的存在,其实就是 Compose 机制下的产物,用以解决recomposition时的值恢复问题。而因为它的“值不变”特性,还可以用来解决耗时计算的问题,即,耗时计算被remember了,那它就只会执行一次,避免了不必要的额外开销

以上就是Android开发Compose remember原理解析的详细内容,更多关于Android开发Compose remember的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android Jetpack Compose无限加载列表

    目录 前言 方法一: paging-compose 方法二:自定义实现 添加 LoadingIndicator 总结 前言 Android 中使用 ListView 或者 RecycleView 经常有滚动到底部自动 LoadMore 的需求,那么在 Compose 中该如何实现呢? 两种方法可供选择: 基于 paging-compose 自定义实现 方法一: paging-compose Jetpack 的 Paging 组件提供了对 Compose 的支持 dependencies { ..

  • Android Jetpack Compose实现列表吸顶效果

    目录 stickyHeader 实体类 加载假数据 吸顶标题 二级条目 完整代码 效果图 安卓传统的 Recyclerview 打造悬浮头部StickyHeader的吸顶效果,十分麻烦,而在Compose中就简单多了 stickyHeader Compose设计的时候考虑得很周到,他们提供了stickyHeader 作用就是添加一个粘性标题项,即使在它后面滚动时也会保持固定.标头将保持固定,直到下一个标头取而代之. 参数key - 表示唯一的密钥键. 它不允许对列表出现使用相同的键.密钥的类型应

  • Android之Compose页面切换动画介绍

    目录 前因后果 开始尝试 开始撸码 仓促的结尾 前因后果 Compose 正式版已经发布了一个多月了,从 Compose beta 版本发布之后各大网站中热度就一直不减,官方也一直在为开发者们推出学习 Compose 的文章,更加说明了 Android 开发的未来趋势. 在之前我写了 Compose 版本的玩安卓,当然也有 MVVM 版本的,只是不同分支而已,这是 Github地址:https://github.com/zhujiang521/PlayAndroid 但之前一直存在着一个问题,就

  • Android Compose实现底部按钮以及首页内容详细过程第1/2页

    目录 前言 Column.Row.ConstraintLayout布局先知 Column纵向排列布局 Row横向排列布局 ConstraintLayout 约束布局 Modifier的简单使用 底部导航栏的实现 首页内容的实现 Banner的实现 首页ViewModel 前言 compose作为Android现在主推的UI框架,各种文章铺天盖地的席卷而来,作为一名Android开发人员也是很有必要的学习一下了,这里就使用wanandroid的开放api来编写一个compose版本的玩安卓客户端,

  • Android Compose自定义TextField实现自定义的输入框

    目录 概览 简单自定义BasicTextField示例 实现自定义样式的BasicTextField 使用BasicTextField自定义百度输入框 概览 众所周知Compose中默认的TextField和OutlineTextField样式并不能满足所有的使用场景,所以自定义TextField就成了必备技能,本文就揭露一下自定义TextField的实现.自定义TextField主要使用BasicTextField实现. 简单自定义BasicTextField示例 1.代码 var text

  • Android Jetpack结构运用Compose实现微博长按点赞彩虹效果

    目录 原版 1. Compose 动画 API 概览 2. 长按点赞动画分解 3. 彩虹动画 3.1 状态管理 AnimatedRainbow animatedRainbows 列表 3.2 内容绘制 4. 表情动画 4.1 状态管理 AnimatedEmoji infiniteRepeatable CubicBezierEasing 抛物线动画 animatedEmojis 列表 4.2 内容绘制 5. 烟花动画 5.1 状态管理 AnimatedFlower keyframes animat

  • Android开发Compose remember原理解析

    目录 正文 随机色文本 原因分析 正确实现 remember的原理剖析 小结 正文 看过Compose案例或者源码的你,相信肯定是见过 remember 了的.顾名思义,Compose是要让我们的代码“记住”东西,那到底是记住什么呢?要是不 remember,相关功能就实现不了了吗? 带着这些问题,来一探究竟吧 随机色文本 假设有这么一个“随机底色文本”的需求:实现一个 Text,其背景色每次启动都随机产生,且生命周期内不变 用Compose可以实现如下: private val items =

  • Android开发Compose集成高德地图实例

    目录 正文 高德地图官网开发者建议 初始化MapView并添加到AndroidView里面 MapView增加一个管理地图生命周期的扩展 给MapView添加生命周期观察者 添加MapView的生命周期控制 正文 Compose中我们应该怎么使用地图呢?像之前我们在xml里面创建MapView,都是在Activity里面,管理MapView生命周期,和其他的监听器,Compose里面怎么搞? 下面我们以高德地图为例,在Compose中创建地图MapView,然后用AndroidView添加Map

  • Android NotificationListenerService 通知服务原理解析

    目录 前言 NotificationListenerService方法集 NotificationListenerService接收流程 通知消息发送流程 NotificationListenerService注册 总结 前言 在上一篇通知服务NotificationListenerService使用方法 中,我们已经介绍了如何使用NotificationListenerService来监听消息通知,在最后我们还模拟了如何实现微信自动抢红包功能. 那么NotificationListenerSe

  • Android开发Compose框架使用开篇

    目录 Compose的诞生 Compose好处 Compose 架构 @Composable的背后 智能重组真的那么智能吗 最后 Compose的诞生 在2019年的谷歌IO大会上,Compose作为Android新一代UI开发亮相,因为声明式开发越来越流行了,对标IOS开发SwiftUi,Compose的立项也为Android开发新加了声明式ui的开发选项,在2021年7月1.0正式版本的诞生,也意味着Compose即将进入生产环节,国际app巨头Twitter就首当其冲,在新页面上用上了Co

  • Android RecyclerView缓存复用原理解析

    目录 一.牵出缓存 1.缓存还在屏幕内的ViewHolder——Scrap缓存 mAttachedScrap mChangeScrap 用一个例子说明 2.缓存屏幕之外的ViewHolder——CacheView 3.mViewCacheExtension 4.RecycledViewPool 二.到底是四级缓存还是三级缓存 一.牵出缓存 都有哪些缓存,作用是什么,为什么这么设计 1.缓存还在屏幕内的ViewHolder——Scrap缓存 Scrap是RecyclerView中最轻量的缓存,它不

  • Android APK应用安装原理解析之AndroidManifest使用PackageParser.parserPackage原理分析

    本文实例讲述了Android APK应用安装之AndroidManifest使用PackageParser.parserPackage原理.分享给大家供大家参考,具体如下: Android 安装一个APK的时候首先会解析APK,这里要做很多事情,其中一个事情就是解析Manifest.xml文件,并将所有APK的Manifest封装到各种对象中并保存在内存当中 解析Manifest的类是非常重要的,该类就是frameworks\base\core\java\android\content\pm\P

  • Android开发之利用jsoup解析HTML页面的方法

    本文实例讲述了Android利用jsoup解析HTML页面的方法.分享给大家供大家参考,具体如下: 这节主要是讲解jsoup解析HTML页面.由于在android开发过程中,不可避免的涉及到web页面的抓取,解析,展示等等,所以,在这里我主要展示下利用jsoup jar包来抓取cnbeta.com网站的话题分类的实例. 下面是主要的代码,由于使用及其简单,我这里就不再多说了: package com.android.web; import java.io.BufferedInputStream;

  • 21天学习android开发教程之XML解析与生成

    本文使用SAX来解析XML,在Android里面可以使用SAX和DOM,DOM需要把整个XML文件读入内存再解析,比较消耗内存,而SAX基于事件驱动的处理方式,可以在各节点触发回调函数,不过SAX适合节点结构简单的XML文档,复杂的XML文档在后期的节点深度处理会有点麻烦. 本文要解析的test.xml文件如下: <?xml version="1.0" encoding="utf-8"?> <test> <title>testSA

  • Spring注解开发生命周期原理解析

    生命周期 initMethod和destroyMethod Bean定义 public class Car { public Car() { System.out.println("car constructor"); } public void init(){ System.out.println("car init"); } public void destroy(){ System.out.println("car destroy"); }

  • Android开发之利用Intent实现数据传递的方法

    本文实例讲述了Android利用Intent实现数据传递的方法.分享给大家供大家参考,具体如下: 在Android开发过程中,很多人都熟悉Intent,这是个用于在多个View之间共享数据的类.本节主要讲述通过点选ListView中的文本,把文本中的URL加载到一个新的页面上,并且打印出来.为了方便,我先把前面一篇<Android开发之利用jsoup解析HTML页面的方法>的代码重新贴一下,因为在上一节后,代码做了少许修改: try { doc = Jsoup.parse(new URL(&q

随机推荐