本篇文章继续分析setChildren方法
- setChildren
- manageChildren
- onBatchComplete
setChildren
JS 端 finalizeInitialChildren 发送 UIManager.setChildren(parentTag, [childTag1, childTag2, ...])。它是 manageChildren 的简化版,只在首次挂载时使用,假设子节点按顺序排列。
与 manageChildren 的对比:
| setChildren | manageChildren | |
|---|---|---|
| 调用时机 | 首次挂载 | 后续增删移动 |
| 支持操作 | 仅添加 | 添加 + 移动 + 删除 |
| 子节点顺序 | 按数组顺序 | 需要指定 index |
| 性能 | 更快(无需排序) | 需要排序和校验 |
ini
public void setChildren(int viewTag, ReadableArray childrenTags) {
synchronized (uiImplementationThreadLock) {
ReactShadowNode cssNodeToManage = mShadowNodeRegistry.getNode(viewTag);
for (int i = 0; i < childrenTags.size(); i++) {
ReactShadowNode cssNodeToAdd = mShadowNodeRegistry.getNode(childrenTags.getInt(i));
if (cssNodeToAdd == null) {
throw new IllegalViewOperationException(
"Trying to add unknown view tag: " + childrenTags.getInt(i));
}
cssNodeToManage.addChildAt(cssNodeToAdd, i);
}
mNativeViewHierarchyOptimizer.handleSetChildren(cssNodeToManage, childrenTags);
}
}
- cssNodeToManage标识查找到的父节点
- cssNodeToManage.addChildAt建立父子节点关系
objectivec
xiao1:ReactNative (UIManager.createView) tag: 3, class: RCTRawText, props: {"text":"App.tsx"}
xiao1:ReactNative (UIManager.createView) tag: 5, class: RCTText, props: {"margin":10,"allowFontScaling":true,"textAlign":"center","ellipsizeMode":"tail","fontSize":20,"accessible":false}
xiao1:ReactNative (UIManager.setChildren) tag: 5, children: [3]
当上面的日志执行完后就会构建出ShadowNode、YogaNode、YGNode节点树:
scss
ShadowNode 树(JS 语义) Yoga 树(布局计算)
┌──────────────┐ ┌──────────────┐
│ View(tag=5) │ │ YogaNode(5) │
│ mChildren: │ │ children: │
│ [3] │ │ [yn3]│
└──────────────┘ └──────────────┘
│ │
┌───┴───┐ ┌───┴───┐
▼ ▼ ▼ ▼
Text(3) YN(3)
继续看handleSetChildren,这个循环对每个子节点调用 addNodeToNode
ini
public void handleSetChildren(ReactShadowNode nodeToManage, ReadableArray childrenTags) {
for (int i = 0; i < childrenTags.size(); i++) {
ReactShadowNode nodeToAdd = mShadowNodeRegistry.getNode(childrenTags.getInt(i));
addNodeToNode(nodeToManage, nodeToAdd, i);
}
}
addNodeToNode中逻辑比较复杂,但是它最终会调用如下代码
csharp
mUIViewOperationQueue.enqueueManageChildren(
parent.getReactTag(),
null,
new ViewAtIndex[] {new ViewAtIndex(child.getReactTag(), index)},
null);
- enqueueManageChildren里面是向mOperations队列添加了一个ManageChildrenOperation
- 看到这里是不是有点熟悉,在讲
setChildren时它最终是向mNonBatchedOperations队列中添加了一个CreateViewOperation
ManageChildrenOperation最终调用mNativeViewHierarchyManager.manageChildren,manageChildren核心作用:负责Native View视图的增删管理
- 当
viewsToAdd不为空时最终是会调用viewManager.addView,本质与常用的addView完全一致
less
public synchronized void manageChildren(
final int tag,
@Nullable int[] indicesToRemove,
@Nullable ViewAtIndex[] viewsToAdd,
@Nullable int[] tagsToDelete) {
...
if (viewsToAdd != null) {
for (int i = 0; i < viewsToAdd.length; i++) {
ViewAtIndex viewAtIndex = viewsToAdd[i];
View viewToAdd = mTagsToViews.get(viewAtIndex.mTag);
int normalizedIndex = viewAtIndex.mIndex;
if (!pendingDeletionTags.isEmpty()) {
normalizedIndex = 0;
int counter = 0;
while (normalizedIndex < viewToManage.getChildCount()) {
if (counter == viewAtIndex.mIndex) {
break;
}
View v = viewToManage.getChildAt(normalizedIndex);
if (!pendingDeletionTags.contains(v.getId())) {
counter++;
}
normalizedIndex++;
}
}
//添加视图
viewManager.addView(viewToManage, viewToAdd, normalizedIndex);
}
}
}
最后总结一下setChildren:它负责关联ShadowNode、YogaNode、YGNode、Native View之间的父子关系,最后形成如下图所示的 4 颗树
【图片原文】

manageChildren
它负责JS视频的增删改,和setChildren分为 2 个核心的:
- ①部分负责更新
ShadowNode、YogaNode、YGNode节点树 - ②部分负责更新Native View节点树
less
public void manageChildren(
int viewTag,
@Nullable ReadableArray moveFrom,
@Nullable ReadableArray moveTo,
@Nullable ReadableArray addChildTags,
@Nullable ReadableArray addAtIndices,
@Nullable ReadableArray removeFrom) {
//①
for (int i = 0; i < viewsToAdd.length; i++) {
ViewAtIndex viewAtIndex = viewsToAdd[i];
ReactShadowNode cssNodeToAdd = mShadowNodeRegistry.getNode(viewAtIndex.mTag);
if (cssNodeToAdd == null) {
throw new IllegalViewOperationException(
"Trying to add unknown view tag: " + viewAtIndex.mTag);
}
cssNodeToManage.addChildAt(cssNodeToAdd, viewAtIndex.mIndex);
}
//②
mNativeViewHierarchyOptimizer.handleManageChildren(
cssNodeToManage, indicesToRemove, tagsToRemove, viewsToAdd, tagsToDelete);
for (int i = 0; i < tagsToDelete.length; i++) {
removeShadowNode(mShadowNodeRegistry.getNode(tagsToDelete[i]));
}
}
}
handleManageChildren中针对每个子节点也是调用addNodeToNode,最后还是向mOperations队列添加了一个ManageChildrenOperation。