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。

相关推荐
BoomHe5 小时前
Android Framework 文件应用移植到 AndroidStudio
android
传奇开心果编程7 小时前
【Jetpack Compose基础语法学与练】第8课 rememberSaveable,页面旋转/系统重建保留状态
android·学习·ui·kotlin·android jetpack
>Andre<7 小时前
UFS5.0标准中文全译·卷一:范围、术语与架构
android·linux·嵌入式硬件
事圆则缓8 小时前
Java 8 Lambda、Stream、Optional 与 Android 边界:从回调语法到运行时兼容
android·java
mmsx8 小时前
Android 地图十万要素不卡顿:空间网格 + 渐进式加载的移动端实践
android·大数据·opengl
Godikov10 小时前
Android 工业终端保活实战:前台服务 + 开机自启 + 更新自启的三重保障
android
字节暗面10 小时前
SO加固强度怎么量化?腾讯ACE与FairGuard静态分析实测
android·逆向
终端安全笔记11 小时前
描述文件、企业证书、MDM 不是三个名字,是三层
android·ios·智能手机
字节暗面12 小时前
一次游戏安全SO静态分析记录:腾讯ACE与FairGuard加固强度对比
android·逆向工程·游戏加固
传奇开心果编程12 小时前
【Jetpack Compose基础语法学与练】第9课 SideEffect副作用 + LaunchedEffect,处理异步任务和生命周期
android·学习·ui·kotlin·android jetpack