使用 Macrobenchmark 测试 Android 应用性能
etpack Compose 是推荐用于构建原生 Android 界面的新工具包。后续简称Jetpack Compose为Compose。
在了解State之前需要先对Compose及申明性编程式有个大概的了解。
State初体验
好了,在你有一定了解的基础上,我们先来运行几个Demo,初步了解为何使用state。这个例子主要是想通过点击按钮,改变文案的显示。(这个过程称之为重组)
Demo1:
classStateDemoActivity : AppCompatActivity() {overridefunonCreate(savedInstanceState: Bundle?) {supportActionBar?.hide()super.onCreate(savedInstanceState)WindowCompat.setDecorFitsSystemWindows(window, false)setContent {TestComposeTheme {var state = falseLog.e("StateDemoActivity", "hashcode:${state.hashCode()} value:${state}")Column(modifier = Modifier.fillMaxSize().systemBarsPadding()) {Button(onClick = {state = !stateLog.e("StateDemoActivity", "onClick hashcode:${state.hashCode()} value:${state}")}, modifier = Modifier.fillMaxWidth()) {Text(text = "Change State:${state}",color = MyColorTheme.textMain,fontSize = 20.sp)}}}}}
}
复制代码
对应输出如下:
StateDemoActivity E hashcode:1237 value:false
StateDemoActivity E onClick hashcode:1231 value:true
StateDemoActivity E onClick hashcode:1237 value:false
StateDemoActivity E onClick hashcode:1231 value:true
StateDemoActivity E onClick hashcode:1237 value:false复制代码
可以发现,点击按钮Text显示的文本没有改变,没有达到预想的目的。
Demo2:这次我们使用官方的MutableState
TestComposeTheme {var state = mutableStateOf(false)Log.e("StateDemoActivity", "hashcode:${state.hashCode()} value:${state.value}")Column(modifier = Modifier.fillMaxSize().systemBarsPadding()) {Button(onClick = {state.value = !state.valueLog.e("StateDemoActivity", "onClick hashcode:${state.hashCode()} value:${state.value}")}, modifier = Modifier.fillMaxWidth()) {Text(text = "Change State:${state.value}",color = MyColorTheme.textMain,fontSize = 20.sp)}}
}
复制代码
输出如下,显示的文案依然没有改变,但相比之前多了Column之前的打印,这表明方法体被重新执行了,state变量的哈希值也在变化。以上表现和普通java方法别无区别,state是方法中的局部变量。
StateDemoActivity E hashcode:103910553 value:false
StateDemoActivity E onClick hashcode:103910553 value:true
StateDemoActivity E hashcode:118168247 value:false
StateDemoActivity E onClick hashcode:118168247 value:true
StateDemoActivity E hashcode:245965755 value:false复制代码
Demo3:这次我们稍作改动,额外使用remember函数,其他不变
var state = remember {mutableStateOf(false)
}
复制代码
输出如下,按钮文字终于如预想的那样发生了变化,此外有个特别的现象是state变量的哈希值并没有发生变化,表明方法每次执行时,state变量并没有重新创建。
StateDemoActivity E hashcode:103910553 value:false
StateDemoActivity E onClick hashcode:103910553 value:true
StateDemoActivity E hashcode:103910553 value:true
StateDemoActivity E onClick hashcode:103910553 value:false
StateDemoActivity E hashcode:103910553 value:false复制代码
remember方法内部必然有全局容器存储变量,源码中可以很明显的看出
@Composable
inline fun <T> remember(crossinline calculation: @DisallowComposableCalls () -> T): T =currentComposer.cache(false, calculation)
复制代码
综上我们可以知道,Compose是依赖对State变化的观察来重新执行Compose方法(准确来说Compose基于参数的比较结果来决定是否重组)
重组与稳定类型
接上文一个 Composable 函数在重组中被调用时,如果参数与上次调用时相比没有发生变化,则函数的执行会跳过重组,提升重组性能。需要特别说明Compose不会因为被观察的对象与上次是同一个就跳过重组。详情参考 Compose类型稳定性注解:@Stable & @Immutable
Composable的重组范围
在之前的内容中,我们已经知道了参数的变化会影响重组是否执行,这就带了重组范围的问题。这方面大佬已经有了很好的文章。建议小伙伴先阅读# Jetpack Compose:理解composable的重组范围
当我阅读后用代码验证时却发现了异常,代码如下
classStateDemoActivity : AppCompatActivity() {privateval TAG = StateDemoActivity::class.java.simpleNameoverridefunonCreate(savedInstanceState: Bundle?) {supportActionBar?.hide()super.onCreate(savedInstanceState)WindowCompat.setDecorFitsSystemWindows(window, false)setContent {TestComposeTheme {Log.e(TAG, "Scope-1 run ")var counter by remember {mutableStateOf(0)}Column(modifier = Modifier.fillMaxSize().systemBarsPadding()) { Log.e(TAG, "Scope-2 run ")Button(onClick = run {Log.e(TAG, "Button-onClick")return@run { counter++ }}) {Log.e(TAG, "Scope-3 run ")Text(text = "+")}Text(text = "Counter:${counter}",color = MyColorTheme.textMain,fontSize = 20.sp)}}}}
}
复制代码
输出如下
StateDemoActivity E Scope-1 run
StateDemoActivity E Scope-2 run
StateDemoActivity E Button-onClick
StateDemoActivity E Scope-3 run
复制代码
按照文章内的说法Scope-3 run 这行是不应该被打印出来的,即不应该参与重组的,可实际结果却相反。小伙伴可以自己思考下,稍后再往下翻看原因;
--------------完美的分割线^_^------------
classStateDemoActivity : AppCompatActivity() {companionobject {privateval TAG = StateDemoActivity::class.java.simpleName}....
}
复制代码
StateDemoActivity E Scope-1 run
StateDemoActivity E Scope-2 run
StateDemoActivity E Button-onClick
复制代码
日志终于和理解的一样了,现在我们回头找原因,知道问题就出在打印的变量“TAG”身上。方法中各作用域Scope都读取了变量“TAG”,在第一个代码中该变量是个非稳定类型,故按钮触发重组时,为了保证正确性,所有引用到该变量的Scope都会重组;而第二个代码中“TAG”已经是个静态变量了,故而是个稳定类型,所以重组时不会引发非必要的重组。
最后补充一点:
从Android View转Compose过程中,对LiveData的使用场景需要特别留意。因为原本对LiveData的观察是通过observe方法进行的,每次LiveData内容更新(即使值一样)回调依然会收到;转换为State之后,只有value变化了,才会引发重组;例如
val missOut = MutableLiveData(false)
...
val state = viewModel.missOut.observeAsState(false).value
复制代码
多次调用missOut.value = true,state只有在第一次设置为true时引发重组。如果希望行为和之前observe一样,应该如下调用:
LaunchedEffect(key1 = Unit) {viewModel.missOut.asFlow().collect {...}
}
复制代码
Android核心知识点笔记:
Android开发核心知识点笔记
Android Framework核心知识点笔记
音视频开发笔记,入门到高级进阶
Android Flutter核心知识点笔记与实战详解
性能调优核心知识点笔记
相关文章:
使用 Macrobenchmark 测试 Android 应用性能
etpack Compose 是推荐用于构建原生 Android 界面的新工具包。后续简称Jetpack Compose为Compose。在了解State之前需要先对Compose及申明性编程式有个大概的了解。State初体验好了,在你有一定了解的基础上,我们先来运行几个Demo,初步了解为何…...

【django】django-simpleui配置后,后台显示空白页解决方法
every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 django后台显示空白页解决方法 1. 正文 添加完simpleui以后,后台显示一片空白,一脸问号??? …...

【035】基于Vue的电商推荐管理系统(含源码数据库、超详细论文)
摘 要:基于Vue+Nodejs+mysql的电商推荐管理系统,这个项目论文超详细,er图、接口文档、功能展示、技术栈等说明特别全!!! (文末附源码数据库、课设论文获取方式࿰…...
【c++】模板1—函数模板
文章目录函数模板语法函数模板注意事项案例—数组选择排序普通函数和函数模板的区别普通函数和函数模板调用规则模板的局限性函数模板语法 函数模板作用: 建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表…...
windows10 wsl子系统固定ip启动分配网卡法
WSL设置添加固定IP 在Win端添加一个固定IP 192.168.50.99 用于X-Server界面显示.在WSL端添加一个固定IP 192.168.50.16 用于和Win端通讯 在win端创建批处理文件 创建一个批处理文件 我的文件位置是D:\powershell\static_ip.bat 向vEthernet (WSL)网卡添加一个IP 192.168.50.…...
ARM+Linux日常开发笔记
ARMLinux开发命令 文章目录ARMLinux开发命令一、虚拟机1.ssh服务项目2.文件相关3.系统相关4. 虚拟机清理内存二、ARM核板1.设备重启三、调试1. 应该调试一、虚拟机 1.ssh服务项目 启动ssh服务 sudo /etc/init.d/ssh restart2.文件相关 查看文件大小显示kb ll -h 查看目录文件…...

在线文档技术-编辑器篇
这是在线文档技术的第二篇文章,本文将对目前市面上所有的主流编辑器和在线文档进行一次深入的剖析和研究,从而使大家对在线文档技术有更深入的了解,也让更多人能够参与其开发与设计中来。 注意:出于对主流文档产品的尊重…...

top -p pid为什么超过100%
CPU:Cores, and Hyper-Threading 超线程(Hyper-Threading ) 超线程是Intel最早提出一项技术,最早出现在2002年的Pentium4上。单个采用超线程的CPU对于操作系统来说就像有两个逻辑CPU,为此P4处理器需要多加入一个Logic…...
#高光谱图像分类#:分类的方法有哪些?
高光谱图像分类方法可以根据分类粒度的不同分为基于像素的分类和基于对象的分类 高光谱图像分类方法可以根据分类粒度的不同分为基于像素的分类和基于对象的分类。 基于像素的分类:这种分类方法是针对每个像素进行分类,将像素的光谱信息作为输入特征&am…...
观察者模式
观察者模式常常用于以下场景:事件驱动系统:当事件发生时,通知所有对该事件感兴趣的观察者。发布/订阅模型:一个主题(发布者)可以有多个订阅者(观察者),当主题发生改变时&…...

前端组件库自定义主题切换探索-03-webpack-theme-color-replacer webpack 同时替换多个颜色改造
接上一篇《前端组件库自定义主题切换探索-02-webpack-theme-color-replacer webpack 的实现逻辑和原理-02》 这篇我们来开始改造,让这个插件最终能达到我们的目的: 首先修改plugin.config.js。 插件首先要在vue.config.js引用注册,因此先对…...

Redis高级-主从复制相关操作
2.1 主从复制简介 2.1.1 高可用 首先我们要理解互联网应用因为其独有的特性我们演化出的三高架构 高并发 应用要提供某一业务要能支持很多客户端同时访问的能力,我们称为并发,高并发意思就很明确了 高性能 性能带给我们最直观的感受就是:速…...

SPI总线设备驱动模型
SPI总线设备驱动模型 文章目录SPI总线设备驱动模型参考资料:一、平台总线设备驱动模型二、 数据结构2.1 SPI控制器数据结构2.2 SPI设备数据结构2.3 SPI设备驱动三、 SPI驱动框架3.1 SPI控制器驱动程序3.2 SPI设备驱动程序致谢参考资料: 内核头文件&…...

开发同事辞职,接手到垃圾代码怎么办?
小王新加入了一家公司,这家公司有点年头,所以连屎山都是发酵过的,味道很冲。和大多数时运不济的程序员一样,到了这种公司,做的大多数工作,就是修补这些祖传代码,为其添砖加瓦。每当被折腾的筋疲…...

gRPC简介
grpc简介 grpc介绍可以参考官网。无论是rpc还是grpc,可以这样理解,都知道过去使用的单单体架构,而在2011年5月威尼斯的一个软件架构会议上提出了微服务架构,围绕业务功能进行组织(organized around business capability)…...

《MySQL系列-InnoDB引擎25》表-InnoDB逻辑存储结构
InnoDB逻辑存储结构 从InnoDB存储引擎的逻辑存储结构看,所有数据都被逻辑地存放在一个空间中,称之为表空间(tablespace)。表空间又由段(segment)、区(extent)、页(page)组成。页在一些文档中有时也称为块(block),InnoDB存储引擎的逻辑存储结构…...

YOLOv8之C2f模块——与YOLOv5的C3模块对比
一、源码对比 YOLOv8完整工程代码下载:ultralytics/ultralytic C2f模块源码在ultralytics/nn/modules.py下,源码如下: class C2f(nn.Module):# CSP Bottleneck with 2 convolutionsdef __init__(self, c1, c2, n1, shortcutFalse, g1, e…...
动态规划实例——换零钱的方法数(C++详解版)
原写了 Java 版本的如何求解换钱的方法数,近期进行了一些细节上的补充,以及部分错误更正,将语言换为了 C 语言。 基础题目 假设你现在拥有不限量的 1 元、5 元、10 元面值纸币,路人甲希望找你换一些零钱,路人甲拿出的…...
linux c
射频驱动 管理硬件设备、分配系统资源 内核由中断服务程序 调度程序 内存管理程序 网络和进程间进程通信程序 linux支持动态加载内核模块 支持多处理smp机制 内核可以抢占preemptive linux系统拥有多个发行版,可能由一个组织 公司和个人发行 VGA兼容或者更…...
第十三章 系统错误消息 - 一般系统错误消息 S - Z
文章目录第十三章 系统错误消息 - 一般系统错误消息 S - Z第十三章 系统错误消息 - 一般系统错误消息 S - Z 错误代码描述<SUBSCRIPT>下标值不合法或Global引用过长。<SWIZZLE FAIL>打开了一个oref,然后试图在另一个无法引用的相关对象中进行搅拌。这可…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...