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。

相关推荐
plainGeekDev16 小时前
文件读写(Java IO)→ Kotlin 扩展函数
android·java·kotlin
s_nshine17 小时前
释放C盘,迁移studio相关数据到其他盘
android·windows·android studio·内存·c盘
韩曙亮17 小时前
【Flutter】Flutter 中的 Android / iOS 特殊配置 ① ( 网络权限配置 | HTTP 明文传输配置 | 应用名称配置 )
android·网络·flutter·http·ios·网络权限
_李小白17 小时前
【android opencv学习笔记】Day 31:提取轮廓之Canny算法
android·opencv·学习
不爱吃糖的程序媛18 小时前
React Native 三方库 react-native-version-number 鸿蒙适配实战:从零到版本信息展示
react native·react.js·harmonyos
Dragon Wu18 小时前
React Native 配置自定义字体
react native·react.js
hashiqimiya18 小时前
每日android布局xml文件
android·xml·gitee
m0_7381207219 小时前
渗透测试基础——PHP 序列化数据结构与反序列化机制详解
android·服务器·网络·数据结构·安全·php
故渊at19 小时前
第二板块:Android 四大组件标准化学理 | 第十一篇:组件间通信(IPC)与 Binder 深度解析
android·binder·组件化·组件间通信