当前位置:网站首页>Is jetpack compose completely out of view?
Is jetpack compose completely out of view?
2022-07-28 13:03:00 【Serbian uncle】
Preface
Compose Official release 1.0 It's been quite a while , But I believe many students are right Compose There are still a lot of confusion Compose With the original View What is the relationship ? with Flutter The same is based entirely on Skia Engine render , Or... Or View The old routine of ?
I believe many students will have the following questions
![[ Picture upload failed ...(image-211dc7-1634021553449)]](/img/0a/647c6e8c30395842d2eaa6a53b8a01.png)
Now let's take a look at the following problem
Phenomenological analysis
Let's first look at such a simple layout
class TestActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setContent {
ComposeBody()
}
}
}
@Composable
fun ComposeBody() {
Column {
Text(text = " This is a line of test data ", color = Color.Black, style = MaterialTheme.typography.h6)
Row() {
Text(text = " Test data 1!", color = Color.Black, style = MaterialTheme.typography.h6)
Text(text = " Test data 2!", color = Color.Black, style = MaterialTheme.typography.h6)
}
}
}
As shown above , It's a simple layout , contain Column,Row And Text
Then we open... In the developer options Display layout boundary , The effect is shown below :
![[ Picture upload failed ...(image-9a3cb4-1634021553449)]](/img/28/390b73b221e865c35c598e3b478a83.png)
We can see Compose The components of show the layout boundaries , We know ,Flutter And WebView H5 The components in the layout will not display the layout boundary , Don't Compose The layout rendering of is actually View babbling ?
Let's go back to onResume Try to traverse View The hierarchy of , to glance at Compose Will it turn into View
override fun onResume() {
super.onResume()
window.decorView.postDelayed({
(window.decorView as? ViewGroup)?.let { transverse(it, 1) }
}, 2000)
}
private fun transverse(view: View, index: Int) {
Log.e("debug", " The first ${index} layer :" + view)
if (view is ViewGroup) {
view.children.forEach { transverse(it, index + 1) }
}
}
Print the hierarchy of the page in the above way , The output is as follows :
E/debug: The first 1 layer :[email protected][RallyActivity]
E/debug: The first 2 layer :android.widget.LinearLayout{4202d0c V.E...... ........ 0,0-1080,2340}
E/debug: The first 3 layer :android.view.ViewStub{2b50655 G.E...... ......I. 0,0-0,0 #10201b1 android:id/action_mode_bar_stub}
E/debug: The first 3 layer :android.widget.FrameLayout{9bfc86a V.E...... ........ 0,90-1080,2340 #1020002 android:id/content}
E/debug: The first 4 layer :androidx.compose.ui.platform.ComposeView{1b4d15b V.E...... ........ 0,0-1080,2250}
E/debug: The first 5 layer :androidx.compose.ui.platform.AndroidComposeView{a8ec543 VFED..... ........ 0,0-1080,2250}
As shown above , We wrote Column,Row,Text It doesn't appear in the layout level , Follow Compose All that matters is ComposeView And AndroidComposeView Two View
and ComposeView And AndroidComposeView It's all in setContent When you add Compose The container of , Let's analyze later , Here is the conclusion
ComposeIt will not be converted toView, But there is only one entranceView, namelyAndroidComposeView
We declare thatComposeThe layout will be converted intoNodeTree,AndroidComposeViewIt will triggerNodeTreeLayout and drawing of
In general ,ComposeThere will be one.ViewEntrance , But its layout and rendering are stillLayoutNodeThe complete , Basically separated fromView
In general , pure Compose The page hierarchy of the page is shown in the figure below :
![[ Picture upload failed ...(image-718043-1634021553449)]](/img/3a/49b4b22d7638c280622d4446c38d5f.png)
Principle analysis
Pre knowledge
We know , stay View There will be a tree in the system ViewTree, Through a tree data structure to describe the whole UI Interface
stay Compose in , The code we write will also be built into a NodeTree, Every component is a ComposeNode, As NodeTree A node on
Compose Yes NodeTree Management involves Applier、Composition and ComposeNode:Composition As a starting point , Initiate the first composition, adopt Compose Implementation , fill Slot Table, And based on Table establish NodeTree. The rendering engine is based on Compose Nodes Rendering UI, whenever recomposition occurs , Will pass Applier Yes NodeTree updated . therefore
ComposeThe execution process is to createNodeAnd buildNodeTreeThe process of .

In order to understand NodeTree The building process of , Let's introduce the following concepts
Applier: Add or delete NodeTree The node of
Simply speaking ,Applier The function of is to add and delete NodeTree The node of , Every NodeTree All operations need to be matched with one Applier.
meanwhile ,Applier A callback will be provided , Based on the callback, we can NodeTree Make custom changes :
interface Applier<N> {
val current: N // Nodes currently being processed
fun onBeginChanges() {}
fun onEndChanges() {}
fun down(node: N)
fun up()
fun insertTopDown(index: Int, instance: N) // Add a node ( The top-down )
fun insertBottomUp(index: Int, instance: N)// Add a node ( Bottom up )
fun remove(index: Int, count: Int) // Delete node
fun move(from: Int, to: Int, count: Int) // Mobile nodes
fun clear()
}
As shown above , When a node is added or deleted, it will call back to Applier in , We can customize the logic when adding or deleting nodes in the callback method , Later, we can take a look at Android platform Compose How to deal with
Composition: Compose Starting point of execution
Composition yes Compose Starting point of execution , Let's look at how to create a Composition
val composition = Composition(
applier = NodeApplier(node = Node()),
parent = Recomposer(Dispatchers.Main)
)
composition.setContent {
// Composable function calls
}
As shown above
CompositionTwo parameters need to be passed in ,ApplierAndRecomposerApplierWe've already talked about that ,RecomposerIt's very important , He is responsible forComposeThe reorganization of , When reorganized ,RecomposerBy callingAppliercompleteNodeTreeChangesComposition#setContentFor subsequentComposeThe call of provides a container
Through the introduction above , We get it NodeTree The basic process of building , Let's analyze setContent Source code
setContent process analysis
setContent entrance
setContent The source code is actually relatively simple , Let's see :
public fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit
) {
// Judge ComposeView Whether there is , If present, do not create
if (existingComposeView != null) with(existingComposeView) {
setContent(content)
} else ComposeView(this).apply {
// take Compose content Add to ComposeView On
setContent(content)
// take ComposeView Add to DecorView On
setContentView(this, DefaultActivityContentLayoutParams)
}
}
Above is setContent Entrance , The main function is to create a ComposeView To add to DecorView On
Composition The creation of
So let's see AndroidComposeView And Composition How it was created
adopt ComposeView#setContent->AbstractComposeView#createComposition->AbstractComposeView#ensureCompositionCreated->ViewGroup#setContent
Finally, the doSetContent Method , Here is the Compose Entrance :Composition Where created
private fun doSetContent(
owner: AndroidComposeView, //AndroidComposeView yes owner
parent: CompositionContext,
content: @Composable () -> Unit
): Composition {
//..
// establish Composition, And pass in Applier And Recomposer
val original = Composition(UiApplier(owner.root), parent)
val wrapped = owner.view.getTag(R.id.wrapped_composition_tag)
as? WrappedComposition
?: WrappedComposition(owner, original).also {
owner.view.setTag(R.id.wrapped_composition_tag, it)
}
// take Compose Content added to Composition in
wrapped.setContent(content)
return wrapped
}
As shown above , The main thing is to create a Composition And pass in UIApplier And Recomposer, And will Compose content Pass in Composition in
UiApplier The implementation of the
It's already created Composition And passed in UIApplier, Later added Node It's all going back to UIApplier in
internal class UiApplier(
root: LayoutNode
) : AbstractApplier<LayoutNode>(root) {
//...
override fun insertBottomUp(index: Int, instance: LayoutNode) {
current.insertAt(index, instance)
}
//...
}
As shown above , When inserting a node , Would call current.insertAt Method , So this current What is it ?
private fun doSetContent(
owner: AndroidComposeView, //AndroidComposeView yes owner
): Composition {
//UiApplier The parameter passed in is AndroidComposeView.root
val original = Composition(UiApplier(owner.root), parent)
}
abstract class AbstractApplier<T>(val root: T) : Applier<T> {
private val stack = mutableListOf<T>()
override var current: T = root
}
}
It can be seen that ,UiApplier The parameter passed in is actually AndroidComposeView Of root, namely current Namely AndroidComposeView Of root
# AndroidComposeView
override val root = LayoutNode().also {
it.measurePolicy = RootMeasurePolicy
//...
}
As shown above ,root It's really just a LayoutNode, We know from above , All nodes will pass Applier Insert into root Next
Layout and drawing entry
Above we are already in AndroidComposeView Get in the NodeTree The root node of , that Compose How is the layout and measurement of triggered ?
# AndroidComposeView
override fun dispatchDraw(canvas: android.graphics.Canvas) {
//Compose Measurement and layout entrance
measureAndLayout()
//Compose Draw the entrance
canvasHolder.drawInto(canvas) { root.draw(this) }
//...
}
override fun measureAndLayout() {
val rootNodeResized = measureAndLayoutDelegate.measureAndLayout()
measureAndLayoutDelegate.dispatchOnPositionedCallbacks()
}
As shown above ,AndroidComposeView Will pass root, Traverse down its child nodes for measurement, layout and drawing , Here is the LayoutNode Draw the entry
Summary
ComposeIn the buildNodeTreeMainly throughComposition,Applier,Recomposerstructure ,ApplierAll nodes will be added toAndroidComposeViewMediumrootUnder the node- stay
setContentIn the process of , Will createComposeViewAndAndroidComposeView, amongAndroidComposeViewyesComposeEntrance AndroidComposeViewstaydispatchDrawChina will passrootTraverse down the child nodes for measurement layout and drawing , Here isLayoutNodeDraw the entry- stay
AndroidOn the platform ,ComposeThe layout and drawing of have been basically separatedViewsystem , But it still depends onCanvas
Compose And cross platform
The above said ,Compose The drawing of still depends on Canvas, But in that case ,Compose How to achieve cross platform ?
This is mainly through good layered design
Compose The code is divided into... From bottom to top 6 layer :
among compose.runtime and compose.compiler Core , They are supporting declarative UI The basis of .
And what we analyzed above AndroidComposeView This part , Belong to compose.ui part , It is mainly responsible for Android Equipment related foundations UI Ability , for example layout、measure、drawing、input etc.
But this part can be replaced ,compose.runtime Provides NodeTree Management and other basic abilities , This part is platform independent , On this basis, each platform only needs to realize UI The rendering of is a complete set of declarative UI frame
be based on compose.runtime You can implement any set of declarative UI frame , About compose.runtime For a detailed introduction, please refer to fundroid Big guy wrote :Jetpack Compose Runtime : declarative UI The basis of
Button In special circumstances
Above we introduced in pure Compose Under the project ,AndroidComposeView There will be no children View, It's traversal LayoutnNode To layout, measure and draw
But if we add one to the code Button, The result may be different
@Composable
fun ComposeBody() {
Column {
Text(text = " This is a line of test data ", color = Color.Black, style = MaterialTheme.typography.h6)
Row() {
Text(text = " Test data 1!", color = Color.Black, style = MaterialTheme.typography.h6)
Text(text = " Test data 2!", color = Color.Black, style = MaterialTheme.typography.h6)
}
Button(onClick = {}) {
Text(text = " This is a Button",color = Color.White)
}
}
}
Then let's look at the hierarchy of the page
E/debug: The first 1 layer :[email protected][RallyActivity]
E/debug: The first 2 layer :android.widget.LinearLayout{397edb1 V.E...... ........ 0,0-1080,2340}
E/debug: The first 3 layer :android.widget.FrameLayout{e2b0e17 V.E...... ........ 0,90-1080,2340 #1020002 android:id/content}
E/debug: The first 4 layer :androidx.compose.ui.platform.ComposeView{36a3204 V.E...... ........ 0,0-1080,2250}
E/debug: The first 5 layer :androidx.compose.ui.platform.AndroidComposeView{a8ec543 VFED..... ........ 0,0-1080,2250}
E/debug: The first 6 layer :androidx.compose.material.ripple.RippleContainer{28cb3ed V.E...... ......I. 0,0-0,0}
E/debug: The first 7 layer :androidx.compose.material.ripple.RippleHostView{b090222 V.ED..... ......I. 0,0-0,0}
You can see , Obviously ,AndroidComposeView There are two more layers under it View, Why is that ?
Let's see RippleHostView Notes
Empty View that hosts a RippleDrawable as its background. This is needed as RippleDrawables cannot currently be drawn directly to a android.graphics.RenderNode (b/184760109), so instead we rely on View’s internal implementation to draw to the background android.graphics.RenderNode. A RippleContainer is used to manage and assign RippleHostViews when needed - see RippleContainer.getRippleHostView.
The meaning is also very simple ,Compose At present, the water ripple effect cannot be drawn directly , Therefore, you need to set the water ripple effect to View The background of , Here use View Made a transit
then RippleHostView And RippleContainer It will naturally be added to AndroidComposeView in , If we were Compose Used in AndroidView, The effect is the same
But this situation does not violate what we said above , pure Compose Under the project ,AndroidComposeView There are no children under the sun View, because Button It's not pure Compose Of
summary
This paper mainly analyzes and answers Compose Is there a complete separation from View The problem of the system , Summarized below :
ComposeIt will not be converted toView, But there is only one entranceView, namelyAndroidComposeView, pureComposeUnder the project ,AndroidComposeViewNo childrenView- We declare that
ComposeThe layout will be converted intoNodeTree,AndroidComposeViewIt will triggerNodeTreeLayout and drawing of ,AndroidComposeView#dispatchDrawIs the entrance to the drawing - stay
AndroidOn the platform ,ComposeThe layout and drawing of have been basically separatedViewsystem , But it still depends onCanvas - Due to the good layering system ,
ComposeIt can be done bycompose.runtimeandcompose.compilerCross platform implementation - In the use of
Buttonwhen ,AndroidComposeViewThere will be two layersView, This is becauseButtonUsed inViewTo achieve the water ripple effect
边栏推荐
- Fundamentals of machine learning - support vector machine svm-17
- [embedded C foundation] Part 1: basic data types
- Review the IO stream again, and have an in-depth understanding of serialization and deserialization
- Huawei cloud Gao Hongxia: CBC microservice code Reconstruction & independent release practice
- AI制药的数据之困,分子建模能解吗?
- CCF201912-2 回收站选址
- Block reversal (summer vacation daily question 7)
- Summary: idea problem record
- Installation and reinstallation of win11 system graphic version tutorial
- Solution to the binary tree problem of niuke.com
猜你喜欢

BiliBili Yang Zhou: above efficiency, efficient delivery

【嵌入式C基础】第2篇:进制转换与BCD编码

如何在 TiDB Cloud 上使用 Databricks 进行数据分析 | TiDB Cloud 使用指南

Interface control telerik UI for WPF - how to use radspreadsheet to record or comment

Change the document type in endnode and import it in word

【C语言易错点】第4篇:结构体在内存中存储规则详讲

How many times can the WordPress user name be changed? Attach the method of changing user name
![[embedded C foundation] Part 2: binary conversion and BCD coding](/img/12/d9a42cf7b4dc177d00e5dc3cdaa5cd.png)
[embedded C foundation] Part 2: binary conversion and BCD coding

CTO of youhaoda, MVP of Huawei cloud, and Zhang Shanyou: build cloud native applications based on kubernetes and dapr

【嵌入式C基础】第5篇:原码/反码/补码
随机推荐
Sliding Window
机器学习实战-决策树-22
Multiple items on a computer share a public-private key pair to pull the Gerrit server code
LeetCode394 字符串解码
黑猫带你学eMMC协议第26篇:eMMC的硬件复位操作(H/W reset)
2020-12-07
Machine learning practice - integrated learning-23
leetcode 1518. 换酒问题
【嵌入式C基础】第3篇:常量和变量
【嵌入式C基础】第9篇:C语言指针的基本用法
Leetcode94. Middle order traversal of binary trees
Leetcode 1518. wine change
C# 泛型是什么、泛型缓存、泛型约束
Leetcode: array
Cloud native - runtime environment
Insufficient permission to pull server code through Jenkins and other precautions
Kotlin是如何帮助你避免内存泄漏的?
Force buckle 315 calculates the number of elements smaller than the current element on the right
Summary: idea problem record
Unity installs the device simulator