最近Jetpack Compose发布了Beta版本,抽时间了解了一下Compose带来的改变和其中的一些原理。本文不会讲解具体API,只是比较随意的分享自己的一些疑问以及在探寻答案过程中的一些收获。

为什么要有Compose?


Android已经十年多了,传统的Android UI ToolKit有很多历史遗留问题,而有些官方也很难修改。比如View.java有三万多行代码,比如Combo box竟然叫Spinner,再比如Button继承自Textview。同时官方的一些widget修复依赖系统升级,到达用户周期过长。

通过在Jetpack中添加Compose,脱离了Android系统,代码修复可以更快地到达用户。

而对国内开发者来说,更统一的代码,意味着没有厂商定制。这几天有位朋友和我抱怨『哪个大佬有时间重写个editText吗,厂商/系统的一堆问题』,我想他可能要梦想成真了。

同时,Compose通过引入声明式编程,依赖Kotlin特性,可以让代码编写更快更简单。

想象写一个搜索通讯录的界面,传统的Android开发写这个界面需要多少代码?activity一个xml,item一个xml,封装一个recyclerview,再写一个Adapter,写了这么多,可能还费力不讨好,xml转成view的过程中,IO和反射影响了性能,界面再复杂一些,走异步layout还是x2c?而在compose中,可能只需要下面这段简短的代码,并且没有xml的性能问题。

如上图,在Compose中,不断的方法调方法,就完成了UI的组装。

什么是声明式编程?

提到Compose,就不得不了解什么是声明式编程。

我们来看一下维基百科的解释,声明式编程是一种编程范式,表达逻辑但不描述具体控制流程。就是告诉计算机我想要什么,而不是告诉它怎么做。那对应到Android就是我想要一个什么样的UI,而不是这个UI应该如何改变,当然UI的自动改变需要框架的支持。

声明式编程在React、Flutter等框架中已经有广泛的应用,声明状态,状态变化,UI自动重绘。

有意思的是,Compose的发起人Jim Sproch之前是React的核心开发人员,他在Slack上聊到VDOM的一些问题,比如vdom分配内存空间在复杂项目中成为性能瓶颈,compose采用调用composable方法的方式,减少内存分配。相比vdom,compose把node隐藏在背后以防滥用,同时可以更方便的使用if/for控制流程。

@Composable是什么原理?

上面这个简单的例子,当点击button,button中的文字自动加1,remember用来记录最新的count值。

@Composable是个注解,而要实现自动更新UI,肯定是修改了Class文件,让我们看看class文件变成了什么样?Kotlin编译后的class在build/tmp/kotlin-classes目录中,但在Android Studio中是无法看到class反编译后的内容,可以用Jadx。然后Text()这些Composable方法编译成class后也有改动,为了方便阅读,最好是编译好APK后,再用Jadx阅读反编译源码。

上面就是编译后的CountInner方法,可以看到,方法参数都被改变了,方法块中添加了很多start/end,调用Text()的Lambda变成了ComposableLamda,改动还是比较多的。

这些改动是怎么实现的呢?如果我没记错的话,Kotlin的协程也做了有些改变方法参数的操作,两个是不是差不多的实现?但协程是kotlin特性,应用层动态修改class文件,难道是在Gradle Transform里用ASM去操纵class的?

一番搜索,发现Compose应用了Kotlin compiler的新特性,通过IR extension,可以在中间代码生成期间修改逻辑。IR又是什么?intermediate representation的缩写,翻译为中间语言。Kotlin为了Compose开放了扩展能力,并且统一了JVM/JS/Native的IR流水线,为跨平台提供支持。可以理解为Kotlin对协程做的那些事情,通过使用IR extension,你在应用层也可以去做了。

Talk is cheap, I will show you the code.

Compose Compiler的源码。ComposePlugin.kt中注册了ComposeIrGenerationextension。ComposeIrGenerationExtension中又有ComposableFunctionBodyTransformer实现上面描述的方法中添加start/end,ComposerLambdaMemoization实现上面描述的改变成ComposerLambda。具体逻辑可以看源码,注释描述的比较清楚。

重组是怎么实现的?

看Compose的文档,一直有重组(Recomposition)这个词,就是状态变化的时候,自动更新UI。那重组是怎么实现的呢?

每次调用count.getValue()的时候,最终会回调到Composer,Composer中维持着一个Map,这时就把state和当前的scope进行了关联,scope可以理解为一段可以重组的范围。那当前的scope哪里来呢?还记得编译的class里多了很多start和end吗,在调用start方法的时候,会生成一个scope,放在栈顶。所以调用count.getValue()的时候,直接拿栈顶scope就可以了。当调用end的时候,会调用updateScope更新scope的block属性,而这个block是一个lambda,执行这个lambda会调用对应的composable方法重绘,这样state和block就关联起来了,后面state变化的时候,拿到block执行就可以了。在这个例子中,count state对应的block是一个调用Button方法的lambda。

再来看下更新state的流程。每次调用count.SetValue()的时候,最终会调到Composer中的recordModificationsOf方法,然后从上段说的Map中获取state对应的scope, 并把它添加到invalidations中,通过编舞者监听,下次vsync时,会调用invalidations中lambda的invoke方法,从而更新UI。

请注意,『在调用start方法的时候,会生成一个scope』,但其实只有第一次添加的时候生成就够了,后面更新UI的时候直接用旧的就可以了,太多类似的东西需要存储,Compose中有一个非常重要的数据结构叫插槽表SlotTable,刚说的这个scope复用以及例子中的remember都是利用了SlotTable,具体可以看深入详解 Jetpack Compose | 实现原理。

Text对应的是TextView吗?

Text对应的是TextView吗?不是的。

debug看了一下,所有的composable UI最后被包在一个AndroidComposeView中,放在ContentView下面,所以最上层的东西是没有变化的。传统的Android UI中的view树,变成了node树,view的那些功能被node替代了。

和旧有体系兼容,可以直接把AndroidComposeView添加在xml中,这样就可以混用了。

自定义Layout怎么写?

简单的看了一下,measure/layout走的是measurePolicy,在一个方法中去写measure和layout。measure中有个Constraints最大最小限制,类似MeasureSpec那一套,match_parent变成了Modifier.fillMaxWidth(),这个Modifier会在measure之前修改Constraints,measure的时候会把修改后的Constraints传递进去。

draw是通过Modifier实现的,还是走canvas那一套。

Touch事件怎么处理?

自以为对Android的touch事件还算比较了解,之前在看Android源码的时候也发现了一些有意思的地方,比如down事件在native底层处理,不是作为message在java层looper处理,所以setMessageLogging的方式检测不到down里的耗时。那编舞者不是分发Input/animation/layout的callback吗?那个主要是用来处理move事件。你以为move事件里只有一个坐标点吗,看看MotionEvent.getHistorySize方法吧,那这个size和屏幕采样率以及触控采样率又是什么关系呢?

言归正传,看到Compose的出现,肯定也好奇对Touch事件处理方式的改变。

dispatchTouchEvent/onInterceptTouchEvent/onTouchEvent这些方法不见了,拦截事件需要实现PointerInputFilter,主要逻辑写在onPointerEvent方法,这个方法甚至连boolean都没返回,那怎么判断是否消费了呢?传递进来包装好的event中有个是否消费的属性,每个filter自己判断是否有未消费的事件,去修改已经消费。感觉这一块还有优化空间,好像没有消费之前的事件,后续事件还会回调到。

现在定义了Initial、Main、Final三个阶段,在你关心的阶段中去处理,前两个阶段和以前差不多,Final阶段类似用来处理之前的cancel事件。

结尾

Compose还在持续优化中,比如composable函数最近要支持并发执行了。

两年磨一剑,谷歌推广Compose的决心是毋庸置疑的。Compose为了方便开发者,也是考虑到了很多现实的东西,比如像kotlin支持和java互调一样,支持Compose和传统UI互调。虽然投入巨大,的确更快更简单,但在社区中的普及还有待时间验证,毕竟Jetpack中的库很多大家都还没有用过,而Compose的征程也注定要比Kotlin艰难。

时间有限,本文只能纸上谈兵、管中窥豹、抛砖引玉了,如有谬误,还望不吝赐教。

技术漫谈之——Jectpack Compose相关推荐

  1. doe报告模板_技术漫谈|关于制剂研发过程中的实验设计(DOE)误区讨论

    技术漫谈 第01期 关于制剂研发过程中的实验设计(DOE)误区讨论 // 作者:合全药业制剂业务后期项目部 感谢合全药业制剂研发后期团队朱莹.夏彬等同事对本稿件的支持 早在2004-2005年,FDA ...

  2. 客户端软件GUI开发技术漫谈:原生与跨平台解决方案分析

    原生开发应用开发 Microsoft阵营的 Winform WinForm是·Net开发平台中对Windows Form的一种称谓. 如果你想深入的美化UI,需要耗费很大的力气,对于目前主流的CSS样 ...

  3. SSO(单点登录)技术漫谈

    目录 1. 名词解释 2. 简介 3. SSO的优势 4. 产品表现 5. 客户需求 6. SSO技术漫谈 7. 基础组件 8. 反向代理SSO 9. 有端SSO 10. 参考资料 1. 名词解释 S ...

  4. 模式识别技术漫谈(1)

    模式识别技术漫谈(1) ------引言 在人工智能技术(Artificial Intelligence)领域中,模式识别(Pattern Recognition)技术也许是最具有挑战性的一门技术了, ...

  5. 模式识别技术漫谈(2)

    模式识别技术漫谈(2) -------大量应用了概率和统计分析方法 模式识别大体上可以分为统计模式识别和句法模式识别,统计模式识别是对大量的样本进行统计或学习而最后得到一个分类器,如贝叶斯分类器.神经 ...

  6. 模式识别技术漫谈(4)

    模式识别技术漫谈(4) ------------关于机器学习 一提到机器学习,首先大家会想到的一定是神经网络,其实机器学习方法很多,这里借用"Learning OpenCV"(Ga ...

  7. 模式识别技术漫谈(3)

    模式识别技术漫谈(3) -------高维空间 我们在表示某个事物的特征时,其特征数一般有三个以上的,甚至有好几百个特征,为了表示方便,对于特征值一般采用向量的形式来表示,所以我们在研究模式识别时会有 ...

  8. 增强现实技术漫谈(续)——研究内容全面解析

    前言 经过上一篇略带诙谐的<增强现实技术漫谈>介绍之后,相信大家已经完全了解了增强现实技术的相关特征和应用领域,并且已经对概念有了清晰的理解了.本文将会针对增强现实(Augmented R ...

  9. 【转】GC、 Wii汉化技术漫谈(上,中) by HyperIris

    最近在研究wii游戏的汉化,这篇文章很不错,所以转载之.不过不知道HyperIris前辈还会不会出下. GC.Wii汉化技术漫谈(上) GC.Wii汉化技术漫谈(中) ---------------- ...

最新文章

  1. 配置jdk环境 windows
  2. 交换机和路由器有哪些区别?
  3. 移动端重构系列1——前期准备工作
  4. Best Cow Line
  5. 修改thymeleaf默认路径
  6. 手写体识别(数据挖掘入门与实践-实验11)
  7. AUTOSAR从入门到精通100讲(十)-DoIP协议介绍
  8. MongoDB聚合—计数count
  9. C++ std::enable_shared_from_this
  10. SpringBoot+Querydsl 框架,大大简化复杂查询操作
  11. XSD文件详解(以Maven为例)
  12. 计算机组策略主要功能,组策略的作用和功能
  13. Diskpart 实现分区自动化
  14. 华为服务器 电源管理 高性能,华为服务器可服务性设计介绍-电源篇-Huawei.PDF
  15. 王者荣耀之我的十天(教你如何10天单排上钻石)
  16. 一个人在家简单吃点小火锅,搭配点小海鲜
  17. unity ugui android 小键盘,Unity inputfield 实现显示 隐藏密码功能(在安卓中切换不打开下虚拟键盘)...
  18. CocosCreator之Button按钮
  19. win7系统下安装win10教程
  20. iOS中 流媒体播放和下载 韩俊强的博客

热门文章

  1. 拼多多Temu出海项目正式上线,教你如何做好测评补单,分得第一杯羹
  2. matlab读Excel表格数据画图,matlab读Excel表格数据画图-matlab如何从excel表格中读取数据?...
  3. 中企海外周报 | 银联业务拓展到171个国家和地区;徐工集团向非洲客户提供定制化设备...
  4. SpringBoot整合liquibase
  5. minecraft1.16java_我的世界:1.16最神奇的种子,自然生成的石头雕像,基岩能用!...
  6. 华为android10手机隐藏小游戏,华为手机10个实用好玩的隐藏功能
  7. 对于线性代数、特征空间、特征提取、深度学习的一些深夜思考
  8. 人均 3.6万行代码, C++ 成最烫手山药:腾讯首度披露技术研发数据!
  9. m4a转如何快速转换为wav格式呢
  10. 为Termux安装图形化界面