ReactNative 源码分析11——Native View创建流程setChildren和manageChildren

本篇文章继续分析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:它负责关联ShadowNodeYogaNodeYGNodeNative View之间的父子关系,最后形成如下图所示的 4 颗树

图片原文

manageChildren

它负责JS视频的增删改,和setChildren分为 2 个核心的:

  • ①部分负责更新ShadowNodeYogaNodeYGNode节点树
  • ②部分负责更新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。

相关推荐
Kapaseker6 分钟前
你是不是还没用过 select?实战 Kotlin select
android·kotlin
__Witheart__1 小时前
适用于Android内核boot.img生成流程
android·rockchip
木易 士心1 小时前
Jetpack Compose 深度解析:初始组合与重组的底层奥秘
android
GitLqr10 小时前
Flutter 无障碍开发实战:玩转 Semantics 解决视障用户使用痛点
android·flutter·dart
雨白13 小时前
掌握 NestedScrolling 嵌套滑动:手写仿知乎折叠主页
android
Xzaveir15 小时前
别把所有“认证”都塞进 AuthService:实名、一键登录与号码身份的领域拆分
android·人工智能
BerrySen17815 小时前
KMP全栈开发:从Android到AI Agent的技术演进与实践
android·人工智能
AFinalStone16 小时前
Android 7系统休眠唤醒(一)电源管理架构全景图
android·powermanager·电源管理
YM52e17 小时前
鸿蒙Flutter Center居中组件:Align对齐详解
android·学习·flutter·华为·harmonyos·鸿蒙
时间的拾荒人18 小时前
MySQL 视图详解
android·数据库·mysql