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。

相关推荐
杉氧2 小时前
深入理解 Compose 重组机制:快照系统如何驱动 UI 精准刷新?
android·架构·android jetpack
召钱熏2 小时前
状态枚举正确≠渲染正确:一个语音按钮的状态机边界修复实录
android·前端
杉氧3 小时前
深度解析:Jetpack Compose 核心架构与底层原理 —— 十年安卓老兵的“破茧重生”
android·架构·android jetpack
通玄3 小时前
Jetpack Compose 入门系列(七):ViewModel 与界面状态管理
android
落魄Android在线炒饭3 小时前
Android Framework 开发技巧:android.jar 生成与系统快速编译验证
android
如此风景4 小时前
Kotlin Flow操作符学习
android·kotlin
plainGeekDev5 小时前
GreenDAO → Room
android·java·kotlin
weiggle5 小时前
第八篇:ViewModel + Compose——生产级状态管理实践
android
恋猫de小郭10 小时前
Amper 正式转正 Kotlin Toolchain ,Gradle 未来何去何从
android·前端·flutter
plainGeekDev11 小时前
ButterKnife → ViewBinding
android·java·kotlin