最后
其实要轻松掌握很简单,要点就两个:
- 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
- 多练。 (视频优势是互动感强,容易集中注意力)
你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。
阿里P7Android高级教程
下面资料部分截图,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。
附送高清脑图,高清知识点讲解教程,以及一些面试真题及答案解析。送给需要的提升技术、近期面试跳槽、自身职业规划迷茫的朋友们。
Android核心高级技术PDF资料,BAT大厂面试真题解析;
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
D/Compose: Button content lambda
D/Compose: Text
按照开发经验,第一感觉会是,text变量只被Text控件用到了。
分析一下,Button控件的定义为:
参数 text
作为表达式执行的调用处是 Button 的尾lambda,而后才作为参数传入 Text()
。 所以此时最小重组范围是 Button 的 尾lambda 而非 Text()
另外还有两点需要关注:
- Compose 关心的是代码块中是否有对 state 的 read,而不是 write。
- text 指向的 MutableState 实例是永远不会变的,变的只是内部的 value
重组中的 Inline 陷阱!
非inline函数 才有资格成为重组的最小范围,理解这点特别重要!
我们将代码稍作改动,为 Text()
包裹一个 Box{...}
@Composable
fun Foo() {
var text by remember { mutableStateOf("") }
Button(onClick = { text = "$text $text" }) {
Log.d(TAG, "Button content lambda")
Box {
Log.d(TAG, "Box")
Text(text).also { Log.d(TAG, "Text") }
}
}
}
日志如下:
D/Compose: Button content lambda
D/Compose: Box
D/Compose: Text
要点:
Column
、Row
、Box
乃至Layout
这种容器类 Composable 都是inline
函数,因此它们只能共享调用方的重组范围,也就是 Button 的 尾lambda
如果你希望通过缩小重组范围提高性能怎么办?
@Composable
fun Foo() {
var text by remember { mutableStateOf("") }
Button(onClick = { text = "$text $text" }) {
Log.d(TAG, "Button content lambda")
Wrapper {
Text(text).also { Log.d(TAG, "Text") }
}
}
}
@Composable
fun Wrapper(content: @Composable () -> Unit) {
Log.d(TAG, "Wrapper recomposing")
Box {
Log.d(TAG, "Box")
content()
}
}
- 自定义非 inline 函数,使之满足 Compose 重组范围最小化条件。
四、Compose开发时,提高性能的关注点
当 Compose 更新重组时,它会经历三个阶段(跟传统View比较类似):
- 组合:Compose 确定要显示的内容 - 运行可组合函数并构建界面树。
- 布局:Compose 确定界面树中每个元素的尺寸和位置。
- 绘图:Compose 实际渲染各个界面元素。
基于这3个阶段, 尽可能从可组合函数中移除计算。每当界面发生变化时,都可能需要重新运行可组合函数;可能对于动画的每一帧,都会重新执行您在可组合函数中放置的所有代码。
1、合理使用 remember
它的作用是:
- 保存重组时的状态,并可以有重组后取出之前的状态
引用官方的栗子🍭:
@Composable
fun ContactList(
contacts: List<Contact>,
comparator: Comparator<Contact>,
modifier: Modifier = Modifier
) {
LazyColumn(modifier) {
// DON’T DO THIS
items(contacts.sortedWith(comparator)) { contact ->
// ...
}
}
}
LazyColumn
在滑动时,会使自身状态发生改变导致ContactList
重组,从而contacts.sortedWith(comparator)
也会重复执行。而排序是一个占用CPU算力的函数,对性能产生了较大的影响。
正确做法:
@Composable
fun ContactList(
contacts: List<Contact>,
comparator: Comparator<Contact>,
modifier: Modifier = Modifier
) {
val sortedContacts = remember(contacts, sortComparator) {
contacts.sortedWith(sortComparator)
}
LazyColumn(modifier) {
items(sortedContacts) {
// ...
}
}
}
- 使用
remember
会对排序的结果进行保存,使得下次重组时,只要contacts
不发生变化 ,其值可以重复使用。 - 也就是说,它只进行了一次排序操作,避免了每次重组时都进行了计算。
提示:
- 更优的做法是将这类计算的操作移出Compose方法,放到ViewModel中,再使用
collectAsState
或LanchEffect
等方式进行观测自动重组。
2、使用LazyColumn、LazyRow
等列表组件时,指定key
如下一段代码,是一个很常见的需求(from官网):
🍔NoteRow记录每项记录的简要信息,当我们进入编辑页进行修改后,需要将最近修改的一条按修改时间放到列表最前面。这时,假若不指定每项Item的Key,其中一项发生了位置变化,都会导致其他的NoteRow
发生重组,然而我们修改的只是其中一项,进行了不必要的渲染。
@Composable
fun NotesList(notes: List<Note>) {
LazyColumn {
items(
items = notes
) { note ->
NoteRow(note)
}
}
}
正确的做法:
- 为每项Item提供 项键,就可避免其他未修改的NoteRow只需挪动位置,避免发生重组
@Composable
fun NotesList(notes: List<Note>) {
LazyColumn {
items(
items = notes,
key = { note ->
// 为每项Item提供稳定的、不会发生改变的唯一值(通常为项ID)
note.id
}
) { note ->
NoteRow(note)
}
}
}
3、使用 derivedStateOf
限制重组
🍗假设我们需要根据列表的第一项是否可见来决定划到顶部的按钮是否可见,代码如下:
val listState = rememberLazyListState()
LazyColumn(state = listState) {
// ...
}
val showButton = listState.firstVisibleItemIndex > 0
AnimatedVisibility(visible = showButton) {
ScrollToTopButton()
}
- 由于列表的滑动会使
listState
状态改变,而使用showButton
的AnimatedVisibility
会不断重组,导致性能下降。
🍟解决方案是使用派生状态。如下 :
val listState = rememberLazyListState()
LazyColumn(state = listState) {
// ...
}
val showButton by remember {
derivedStateOf {
listState.firstVisibleItemIndex > 0
}
}
AnimatedVisibility(visible = showButton) {
ScrollToTopButton()
}
- 派生状态,可以这样理解,只有在
derivedStateOf
里的状态发生改变时,只关注和派发对UI界面产生了影响的状态。这样AnimatedVisibility
只会在改变时发生重组。对应的应用场景是,状态发生了改变,但是我们只关注对界面产生了影响的状态进行分发,这种情况下,就可以考虑使用。
4、尽可能延迟State的读行为
之前我们提到,对于一个Compose页面来说,它会经历以下步骤:
- 第一步,Composition,这其实就代表了我们的Composable函数执行的过程。
建议
当我们出去找工作,或者准备找工作的时候,我们一定要想,我面试的目标是什么,我自己的技术栈有哪些,近期能掌握的有哪些,我的哪些短板 ,列出来,有计划的去完成,别看前两天掘金一些大佬在驳来驳去 ,他们的观点是他们的,不要因为他们的观点,膨胀了自己,影响自己的学习节奏。基础很大程度决定你自己技术层次的厚度,你再熟练框架也好,也会比你便宜的,性价比高的替代,很现实的问题但也要有危机意识,当我们年级大了,有哪些亮点,与比我们经历更旺盛的年轻小工程师,竞争。
-
无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!!!!!!!
-
准备想说怎么样写简历,想象算了,我觉得,技术就是你最好的简历
-
我希望每一个努力生活的it工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。
-
有什么问题想交流,欢迎给我私信,欢迎评论
【附】相关架构及资料
内含往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
内含往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!