ReactNative Fabric渲染器和组件(5)

news2024/12/29 16:06:03

ReactNative Fabric渲染器和组件

简述

Fabric是ReactNative中新架构最核心的模块,本章我们会来了解一下自定义一个Fabric组件,然后在JS文件中声明之后如何,是怎么映射到原生构建一个View的。
关于Fabric架构理念官网已经有说明了,我们就不细说了,从实现来说,就是在JS层会有一套DOM树,这个是基于React实现的,然后在C++层会构建出一个ShadowTree,由一个个ShadowNode组成的树,最终会映射到原生的View。

Demo

和TurboModule一样,我们也先按照官网流程生成一个自定义的Fabric组件,然后后面我们在JS中去使用它,最终来看看它的构建流程。

我们定义一个JS文件,然后使用使用Codegen生成脚手架。

// @flow strict-local

import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes';
import type {HostComponent} from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';

type NativeProps = $ReadOnly<{|
    ...ViewProps,
    text: ?string,
    // add other props here
|}>;

export default (codegenNativeComponent<NativeProps>(
'RTNTestView',
): HostComponent<NativeProps>);

生成的代码如下:
在这里插入图片描述

入口

我们从index.js开始看,我们会省略一些React的逻辑,主要是看一下RN在Android上的流程,而对React对DOM对构建和管理不是我们所关心的。

1.1 index.js
入口调用了AppRegistry.registerComponent,() => App是我们写UI布局的地方,我们知道RN使用了JSX语法,我们 这样的UI标签本质其实是React.createElement(“Text”,…),所以我们是可以通过这个函数获取ReactElement的,这一部分的构建属于React的,我们不深入研究。

AppRegistry.registerComponent(appName, () => App);

1.2 AppRegistry.registerComponent
主要是调用了renderApplication

registerComponent(
    appKey: string,
    componentProvider: ComponentProvider,
    section?: boolean,
): string {
    const scopedPerformanceLogger = createPerformanceLogger();
    runnables[appKey] = (appParameters, displayMode) => {
    const concurrentRootEnabled = Boolean(
        appParameters.initialProps?.concurrentRoot ||
        appParameters.concurrentRoot,
    );
    // 详见1.3
    renderApplication(
        componentProviderInstrumentationHook(
            componentProvider,
            scopedPerformanceLogger,
        ),
        appParameters.initialProps,
        appParameters.rootTag,
        wrapperComponentProvider && wrapperComponentProvider(appParameters),
        rootViewStyleProvider && rootViewStyleProvider(appParameters),
        appParameters.fabric,
        scopedPerformanceLogger,
        appKey === 'LogBox', // is logbox
        appKey,
        displayMode,
        concurrentRootEnabled,
    );
    };
    if (section) {
        sections[appKey] = runnables[appKey];
    }
    return appKey;
}

1.3 renderApplication

export default function renderApplication<Props: Object>(
    RootComponent: React.ComponentType<Props>,
    initialProps: Props,
    rootTag: any,
    WrapperComponent?: ?React.ComponentType<any>,
    rootViewStyle?: ?ViewStyleProp,
    fabric?: boolean,
    scopedPerformanceLogger?: IPerformanceLogger,
    isLogBox?: boolean,
    debugName?: string,
    displayMode?: ?DisplayModeType,
    useConcurrentRoot?: boolean,
    useOffscreen?: boolean,
    ) {
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);

const performanceLogger = scopedPerformanceLogger ?? GlobalPerformanceLogger;

// 这里将传入的RootComponent外面包了两层
let renderable: React.MixedElement = (
    <PerformanceLoggerContext.Provider value={performanceLogger}>
    <AppContainer
        rootTag={rootTag}
        fabric={fabric}
        WrapperComponent={WrapperComponent}
        rootViewStyle={rootViewStyle}
        initialProps={initialProps ?? Object.freeze({})}
        internal_excludeLogBox={isLogBox}>
        <RootComponent {...initialProps} rootTag={rootTag} />
    </AppContainer>
    </PerformanceLoggerContext.Provider>
);

    // ...debug相关操作

    if (useOffscreen && displayMode != null) {
        // $FlowFixMe[incompatible-type]
        // $FlowFixMe[prop-missing]
        const Activity: ActivityType = React.unstable_Activity;

        renderable = (
        <Activity
            mode={displayMode === DisplayMode.VISIBLE ? 'visible' : 'hidden'}>
            {renderable}
        </Activity>
        );
    }

    // We want to have concurrentRoot always enabled when you're on Fabric.
    const useConcurrentRootOverride = fabric;

    // ... 性能日志
    // 渲染element,renderable是我们在app.js定义的UI并且在外面套上AppContainer和PerformanceLoggerContext.Provider生成的Element树的根节点。
    // 详见1.4
    Renderer.renderElement({
        element: renderable,
        rootTag,
        useFabric: Boolean(fabric),
        useConcurrentRoot: Boolean(useConcurrentRootOverride),
    });
    // ... 性能日志
}

1.4 renderElement
这里就是判断一下使用新渲染器Fabric还是以前的渲染器,我们这里跟Fabric的。
ReactFabric会根据是否是dev(debug)来决定使用ReactFabric-dev还是ReactFabric-prod,区别就是dev会有一些调试能力。
我们来看ReactFabric-prod里的render。

export function renderElement({
    element,
    rootTag,
    useFabric,
    useConcurrentRoot,
    }: {
    element: React.MixedElement,
    rootTag: number,
    useFabric: boolean,
    useConcurrentRoot: boolean,
    }): void {
    if (useFabric) {
        // 详见1.5
        require('../Renderer/shims/ReactFabric').render(
            element,
            rootTag,
            null,
            useConcurrentRoot,
            {
                onCaughtError,
                onUncaughtError,
                onRecoverableError,
            },
        );
    } else {
        require('../Renderer/shims/ReactNative').render(
            element,
            rootTag,
            undefined,
            {
                onCaughtError,
                onUncaughtError,
                onRecoverableError,
            },
        );
    }
}

1.5 render
这个文件里逻辑也可以不用太关心,这里主要是处理树的数据结构构造和更新,加上源码里一堆魔数,看不懂~,主要看一下流程。
接下来我们直接跳到这个文件里创建ShadowNode的地方。

exports.render = function (
    element,
    containerTag,
    callback,
    concurrentRoot,
    options
    ) {
    // roots是一个Map缓存
    var root = roots.get(containerTag);
    // 如果之前没有这个root节点,需要新建
    if (!root) {
        root = nativeOnUncaughtError;
        var onCaughtError = nativeOnCaughtError,
        onRecoverableError = defaultOnRecoverableError;
        options &&
            void 0 !== options.onUncaughtError &&
            (root = options.onUncaughtError);
        options &&
            void 0 !== options.onCaughtError &&
            (onCaughtError = options.onCaughtError);
        options &&
            void 0 !== options.onRecoverableError &&
            (onRecoverableError = options.onRecoverableError);
        concurrentRoot = concurrentRoot ? 1 : 0;
        // 新建Fiber root节点
        options = new FiberRootNode(
            containerTag,
            concurrentRoot,
            !1,
            "",
            root,
            onCaughtError,
            onRecoverableError,
            null
        );
        // 新建Fiber节点
        concurrentRoot = createFiber(3, null, null, 1 === concurrentRoot ? 1 : 0);
        options.current = concurrentRoot;
        concurrentRoot.stateNode = options;
        root = createCache();
        root.refCount++;
        options.pooledCache = root;
        root.refCount++;
        concurrentRoot.memoizedState = {
            element: null,
            isDehydrated: !1,
            cache: root
        };
        initializeUpdateQueue(concurrentRoot);
        root = options;
        roots.set(containerTag, root);
    }
    // 触发更新
    updateContainer(element, root, null, callback);
    a: if (((element = root.current), element.child))
        switch (element.child.tag) {
        case 27:
        case 5:
            element = getPublicInstance(element.child.stateNode);
            break a;
        default:
            element = element.child.stateNode;
        }
    else element = null;
    return element;
};

1.6 completeWork
调用createNode来创建Shadow节点,uiViewClassName则是我们最早在JS中声明的,以我们自定义的Fabric组件为例就是RTNTestView
这里的createNode是nativeFabricUIManager里的createNode,而nativeFabricUIManager是一个native对象,通过JSI加载进来的,加载的代码在UIManagerBinding.cpp中,看过之前TurboModule加载和JSI的章节的话,很容易可以看懂这里的逻辑,详见1.6.1

function completeWork(current, workInProgress, renderLanes) {
    // ...
    // 详见1.7
    oldProps = createNode(
        current,
        renderLanes.uiViewClassName,
        oldProps,
        updatePayload,
        workInProgress
        );
    // ...
}

nativeFabricUIManage = nativeFabricUIManager,
createNode = _nativeFabricUIManage.createNode,

1.6.1 UIManagerBinding::createAndInstallIfNeeded
这里就是将JS nativeFabricUIManager和UIManagerBinding关联,所以我们要来看一下UIManagerBinding的get方法,看一下createNode是怎么定义的。

void UIManagerBinding::createAndInstallIfNeeded(
    jsi::Runtime& runtime,
    const std::shared_ptr<UIManager>& uiManager) {
    auto uiManagerModuleName = "nativeFabricUIManager";

    auto uiManagerValue =
        runtime.global().getProperty(runtime, uiManagerModuleName);
    // 如果之前已经设置过了不重复设置
    if (uiManagerValue.isUndefined()) {
        auto uiManagerBinding = std::make_shared<UIManagerBinding>(uiManager);
        auto object = jsi::Object::createFromHostObject(runtime, uiManagerBinding);

        // 通过JSI接口设置全局global的属性nativeFabricUIManager为native的uiManagerBinding
        runtime.global().setProperty(
            runtime, uiManagerModuleName, std::move(object));
    }
}

1.7 UIManagerBinding::get
createNode调用了uiManager->createNode来构造ShadowNode,然后把ShadowNode封装到JSI::Vaule返回给JS层。
这里的uiManager是一个UIManager实例,UIManagerBinding和UIManager是在FabricUIManager模块加载时,加载SurfaceManager,里面构造Schudler的时候进行绑定的。

jsi::Value UIManagerBinding::get(
    jsi::Runtime& runtime,
    const jsi::PropNameID& name) {
    auto methodName = name.utf8(runtime);

    UIManager* uiManager = uiManager_.get();

    if (methodName == "createNode") {
        auto paramCount = 5;
        return jsi::Function::createFromHostFunction(
            runtime,
            name,
            paramCount,
            [uiManager, methodName, paramCount](
                jsi::Runtime& runtime,
                const jsi::Value& /*thisValue*/,
                const jsi::Value* arguments,
                size_t count) -> jsi::Value {
                try {
                    validateArgumentCount(runtime, methodName, paramCount, count);

                    auto instanceHandle =
                        instanceHandleFromValue(runtime, arguments[4], arguments[0]);
                    if (!instanceHandle) {
                        react_native_assert(false);
                        return jsi::Value::undefined();
                    }
                    // 把构造的ShadowNode封装成JSI::Vaule以便返回给JS层
                    return valueFromShadowNode(
                        runtime,
                        // 调用的uiManager->createNode来构造ShadowNode,详见1.8
                        uiManager->createNode(
                            tagFromValue(arguments[0]),
                            stringFromValue(runtime, arguments[1]),
                            surfaceIdFromValue(runtime, arguments[2]),
                            RawProps(runtime, arguments[3]),
                            std::move(instanceHandle)),
                        true);
                } catch (const std::logic_error& ex) {
                    LOG(FATAL) << "logic_error in createNode: " << ex.what();
                }
            });
    }
    // ...
}

1.8 UIManager::createNode
这里就是真正构建shadowNode的地方,回过头去看一下Codegen生成的脚手架,会发现这个方法里用到的好多类在刚刚都见过。
componentDescriptor,Props,State,ShadowNode。
后面我们结合脚手架生成的代码来看看这些类的作用。

std::shared_ptr<ShadowNode> UIManager::createNode(
    Tag tag,
    const std::string& name,
    SurfaceId surfaceId,
    RawProps rawProps,
    InstanceHandle::Shared instanceHandle) const {
    SystraceSection s("UIManager::createNode", "componentName", name);
    // 我们每一个Fabric控件都会有一个ComponentDescriptor,然后会根据组件的名称注册到componentDescriptorRegistry_中去
    // 这里我们就可以通过组件名称获取ComponentDescriptor  
    // 我们一会会介绍,根据我们自定义的组件来看的话,这里最终获取的是RTNTestViewComponentDescriptor
    auto& componentDescriptor = componentDescriptorRegistry_->at(name);
    auto fallbackDescriptor =
        componentDescriptorRegistry_->getFallbackComponentDescriptor();

    PropsParserContext propsParserContext{surfaceId, *contextContainer_.get()};

    auto family = componentDescriptor.createFamily(
        {tag, surfaceId, std::move(instanceHandle)});
    const auto props = componentDescriptor.cloneProps(
        propsParserContext, nullptr, std::move(rawProps));
    const auto state = componentDescriptor.createInitialState(props, family);

    // 详见3.1
    auto shadowNode = componentDescriptor.createShadowNode(
        ShadowNodeFragment{
            .props = fallbackDescriptor != nullptr &&
                    fallbackDescriptor->getComponentHandle() ==
                        componentDescriptor.getComponentHandle()
                ? componentDescriptor.cloneProps(
                        propsParserContext,
                        props,
                        RawProps(folly::dynamic::object("name", name)))
                : props,
            .children = ShadowNodeFragment::childrenPlaceholder(),
            .state = state,
        },
        family);

    if (delegate_ != nullptr) {
        // 详见3.2
        delegate_->uiManagerDidCreateShadowNode(*shadowNode);
    }
    if (leakChecker_) {
        leakChecker_->uiManagerDidCreateShadowNodeFamily(family);
    }

    return shadowNode;
}

脚手架代码

我们先来看一下脚手架生成的代码,一会接着上面createNode。

我们先看看注册ComponentDescriptor的流程,在第一章启动流程中,介绍过application会调用getDefaultReactHost构造ReactHost。

2.1 getDefaultReactHost
我们这里只关注注册ComponentDescriptor流程,其他的之前介绍启动流程的时候介绍过了。
这里调用DefaultComponentsRegistry.register

@OptIn(UnstableReactNativeAPI::class)
@JvmStatic
public fun getDefaultReactHost(
    context: Context,
    packageList: List<ReactPackage>,
    jsMainModulePath: String = "index",
    jsBundleAssetPath: String = "index",
    isHermesEnabled: Boolean = true,
    useDevSupport: Boolean = ReactBuildConfig.DEBUG,
    cxxReactPackageProviders: List<(ReactContext) -> CxxReactPackage> = emptyList(),
): ReactHost {
    //...
    val componentFactory = ComponentFactory()
    // 详见2.2
    DefaultComponentsRegistry.register(componentFactory)
    // ...
}

2.2 DefaultComponentsRegistry.register
构造DefaultComponentsRegistry

public fun register(componentFactory: ComponentFactory): DefaultComponentsRegistry =
    DefaultComponentsRegistry(componentFactory)

2.3 DefaultComponentsRegistry
构造的时候会调用initHybrid,initHybrid是一个native方法。

private val mHybridData: HybridData = initHybrid(componentFactory)

@DoNotStrip private external fun initHybrid(componentFactory: ComponentFactory): HybridData

2.4 initHybrid

jni::local_ref<DefaultComponentsRegistry::jhybriddata>
DefaultComponentsRegistry::initHybrid(
    jni::alias_ref<jclass>,
    ComponentFactory* delegate) {
    auto instance = makeCxxInstance(delegate);

    auto buildRegistryFunction =
        [](const EventDispatcher::Weak& eventDispatcher,
            const ContextContainer::Shared& contextContainer)
        -> ComponentDescriptorRegistry::Shared {
        ComponentDescriptorParameters params{
            .eventDispatcher = eventDispatcher,
            .contextContainer = contextContainer,
            .flavor = nullptr};

        // buildRegistryFunction会调用sharedProviderRegistry,详见2.5
        auto registry = DefaultComponentsRegistry::sharedProviderRegistry()
                            ->createComponentDescriptorRegistry(params);

        auto& mutableRegistry = const_cast<ComponentDescriptorRegistry&>(*registry);
        mutableRegistry.setFallbackComponentDescriptor(
            std::make_shared<UnimplementedNativeViewComponentDescriptor>(params));

        return registry;
    };

    delegate->buildRegistryFunction = buildRegistryFunction;
    return instance;
}

2.5 buildRegistryFunction

std::shared_ptr<const ComponentDescriptorProviderRegistry>
    DefaultComponentsRegistry::sharedProviderRegistry() {
    // 这里注册的是RN的核心模块
    auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
    // 这里注册我们自定义的模块,详见2.6 
    if (DefaultComponentsRegistry::registerComponentDescriptorsFromEntryPoint) {
        (DefaultComponentsRegistry::registerComponentDescriptorsFromEntryPoint)(
            providerRegistry);
    } else {
        LOG(WARNING)
            << "Custom component descriptors were not configured from JNI_OnLoad";
    }

    return providerRegistry;
}

2.6 DefaultComponentsRegistry::registerComponentDescriptorsFromEntryPoint
这里的registerComponentDescriptorsFromEntryPoint是facebook::react::registerComponents

void registerComponents(
    std::shared_ptr<const ComponentDescriptorProviderRegistry> registry) {
        // ...
        // 详见2.7
        autolinking_registerProviders(registry);
}

2.7 autolinking_registerProviders
这里调用add来注册ComponentDescriptor
RTNTestViewComponentDescriptor是脚手架生成的,concreteComponentDescriptorProvider是一个模板方法,其中生成ComponentDescriptorProvider中constructor可以构造RTNTestViewComponentDescriptor

void autolinking_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry) {
    providerRegistry->add(concreteComponentDescriptorProvider<RTNTestViewComponentDescriptor>());
    return;
}

2.8 ComponentDescriptorRegistry::add
这里调用constructor,会构造RTNTestViewComponentDescriptor,然后根据名字和句柄分别存储在两个map中。

void ComponentDescriptorRegistry::add(
    ComponentDescriptorProvider componentDescriptorProvider) const {
    std::unique_lock lock(mutex_);

    auto componentDescriptor = componentDescriptorProvider.constructor(
        {parameters_.eventDispatcher,
        parameters_.contextContainer,
        componentDescriptorProvider.flavor});
    // ...

    auto sharedComponentDescriptor = std::shared_ptr<const ComponentDescriptor>(
        std::move(componentDescriptor));
    _registryByHandle[componentDescriptorProvider.handle] =
        sharedComponentDescriptor;
    _registryByName[componentDescriptorProvider.name] = sharedComponentDescriptor;
}

2.9 RTNTestViewComponentDescriptor
RTNTestViewComponentDescriptor是一个ConcreteComponentDescriptor,ConcreteComponentDescriptor封装了组件的信息,包括shadowNode,state,prop等。
而RTNTestViewShadowNode则是一个ConcreteViewShadowNode模板类,里面会包含所RTNTestViewProps,RTNTestViewEventEmitter,RTNTestViewState,这样脚手架就实现了一个自定义Fabric组件然后关联组件自己的Props,State,EventEmitter。

using RTNTestViewComponentDescriptor = ConcreteComponentDescriptor<RTNTestViewShadowNode>;

using RTNTestViewShadowNode = ConcreteViewShadowNode<
    RTNTestViewComponentName,
    RTNTestViewProps,
    RTNTestViewEventEmitter,
    RTNTestViewState>;

接着第一节创建ShadowNode

3.1 RTNTestViewComponentDescriptor.createShadowNode
构建了一个RTNTestViewShadowNode

std::shared_ptr<ShadowNode> createShadowNode(
    const ShadowNodeFragment& fragment,
    const ShadowNodeFamily::Shared& family) const override {
    // ShadowNodeT是模板参数,RTNTestViewShadowNode,所以这里构建了一个RTNTestViewShadowNode
    auto shadowNode =
        std::make_shared<ShadowNodeT>(fragment, family, getTraits());

    adopt(*shadowNode);

    return shadowNode;
}

3.2 uiManagerDidCreateShadowNode
这里也是接着1.8的,从delegate_->uiManagerDidCreateShadowNode过来。

void Scheduler::uiManagerDidCreateShadowNode(const ShadowNode& shadowNode) {
    if (delegate_ != nullptr) {
        // 详见3.3
        delegate_->schedulerDidRequestPreliminaryViewAllocation(shadowNode);
    }
}

3.3 FabricUIManagerBinding::schedulerDidRequestPreliminaryViewAllocation
调用了mountingManager调用maybePreallocateShadowNode,构造好的shadowNode后续需要mount挂载,挂载之后就会构建原生的View。

void FabricUIManagerBinding::schedulerDidRequestPreliminaryViewAllocation(
    const ShadowNode& shadowNode) {
    auto mountingManager = getMountingManager("preallocateView");
    if (!mountingManager) {
        return;
    }
    // 详见3.4 
    mountingManager->maybePreallocateShadowNode(shadowNode);

    // ...
}

3.4 FabricMountingManager::maybePreallocateShadowNode
这里构建了ShadowView,然后调用了preallocateShadowView

void FabricMountingManager::maybePreallocateShadowNode(
    const ShadowNode& shadowNode) {
    if (!shadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView)) {
        return;
    }

    static thread_local bool onMainThread = isOnMainThread();
    if (onMainThread) {
        // View preallocation is not beneficial when rendering on the main thread
        return;
    }
    // 构造ShadowView
    auto shadowView = ShadowView(shadowNode);

    if (ReactNativeFeatureFlags::useOptimisedViewPreallocationOnAndroid()) {
        std::lock_guard lock(preallocateMutex_);
        preallocatedViewsQueue_.push_back(std::move(shadowView));
    } else {
        // 详见3.5
        preallocateShadowView(shadowView);
    }
}

3.5 FabricMountingManager::preallocateShadowView
每个surfaceId会映射一个队列,队列里会记录所有使用过的View的TAG,然后调用preallocateView,preallocateView映射java层的方法。

void FabricMountingManager::preallocateShadowView(
    const ShadowView& shadowView) {
    SystraceSection section("FabricMountingManager::preallocateShadowView");
    
    {
        // 每个SurfaceId会映射一个数组,数组里记录使用到的View的TAG
        std::lock_guard lock(allocatedViewsMutex_);
        auto allocatedViewsIterator =
            allocatedViewRegistry_.find(shadowView.surfaceId);
        if (allocatedViewsIterator == allocatedViewRegistry_.end()) {
            return;
        }
        auto& allocatedViews = allocatedViewsIterator->second;
        if (allocatedViews.find(shadowView.tag) != allocatedViews.end()) {
            return;
        }
        allocatedViews.insert(shadowView.tag);
    }

    bool isLayoutableShadowNode = shadowView.layoutMetrics != EmptyLayoutMetrics;

    // 映射java的方法,preallocateView
    static auto preallocateView =
        JFabricUIManager::javaClassStatic()
            ->getMethod<void(jint, jint, jstring, jobject, jobject, jboolean)>(
                "preallocateView");


    // ...处理Prop和State

    auto component = getPlatformComponentName(shadowView);
    // 调用preallocateView,这里会调用到java的preallocateView,详见3.6 
    preallocateView(
        javaUIManager_,
        shadowView.surfaceId,
        shadowView.tag,
        component.get(),
        props.get(),
        (javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr),
        isLayoutableShadowNode);
}

3.6 SurfaceMountingManager.preallocateView
调用createViewUnsafe构建View

@UiThread
public void preallocateView(
    @NonNull String componentName,
    int reactTag,
    @Nullable ReadableMap props,
    @Nullable StateWrapper stateWrapper,
    boolean isLayoutable) {
    UiThreadUtil.assertOnUiThread();

    if (isStopped()) {
        return;
    }
    if (getNullableViewState(reactTag) != null) {
        return;
    }
    // 详见3.7
    createViewUnsafe(componentName, reactTag, props, stateWrapper, null, isLayoutable);
}

3.7 createViewUnsafe

@UiThread
public void createViewUnsafe(
    @NonNull String componentName,
    int reactTag,
    @Nullable ReadableMap props,
    @Nullable StateWrapper stateWrapper,
    @Nullable EventEmitterWrapper eventEmitterWrapper,
    boolean isLayoutable) {
    Systrace.beginSection(
        Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
        "SurfaceMountingManager::createViewUnsafe(" + componentName + ")");
    try {
        ReactStylesDiffMap propMap = new ReactStylesDiffMap(props);
        // 将View的核心要素Prop,State,EventEmitter封装到ViewState
        ViewState viewState = new ViewState(reactTag);
        viewState.mCurrentProps = propMap;
        viewState.mStateWrapper = stateWrapper;
        viewState.mEventEmitter = eventEmitterWrapper;
        mTagToViewState.put(reactTag, viewState);

        if (isLayoutable) {
            ViewManager viewManager = mViewManagerRegistry.get(componentName);
            // 调用ViewManager.createView,这里的ViewManager每个Fabric组件都有一个
            // 以我们实现的RTNTestViewManager为例来看看,详见3.8
            viewState.mView =
                viewManager.createView(
                    reactTag, mThemedReactContext, propMap, stateWrapper, mJSResponderHandler);
            viewState.mViewManager = viewManager;
        }
    } finally {
        Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }
}

3.8 RTNTestViewManager
这里我们实现的RTNTestViewManager继承自SimpleViewManager,而SimpleViewManager继承自BaseViewManager,BaseViewManager继承自ViewManager。
ViewManager的createView会调用createViewInstance,同时createView会更新Prop,State,EventEmitter。
而这里createViewInstance就会构建View。

@ReactModule(name = RTNTestViewManager.NAME)
public class RTNTestViewManager extends SimpleViewManager<TextView>
        implements RTNTestViewManagerInterface<TextView> {

    private final ViewManagerDelegate<TextView> mDelegate;

    static final String NAME = "RTNTestView";

    public RTNTestViewManager(ReactApplicationContext context) {
        mDelegate = new RTNTestViewManagerDelegate<>(this);
    }

    @Nullable
    @Override
    protected ViewManagerDelegate<TextView> getDelegate() {
        return mDelegate;
    }

    @NonNull
    @Override
    public String getName() {
        return RTNTestViewManager.NAME;
    }

    @NonNull
    @Override
    protected TextView createViewInstance(@NonNull ThemedReactContext context) {
        // 返回自定义的View,这里只是随便返回一个TextView,实际是要返回一个自定义的View
        return new TextView(context);
    }

    @Override
    @ReactProp(name = "text")
    public void setText(TextView view, @Nullable String text) {
        view.setText(text);
    }
}

其他

  • Props
    Props继承了ViewProps,之所以要有继承是因为需要实现一些基础的属性,ViewProps本质是BaseViewProps,BaseViewProps继承了YogaStylableProps和AccessibilityProps,实现基础属性,比如有宽度高度等等基础属性,这样我们自定义的Fabric就只需要定义我们额外的属性。

    class RTNTestViewProps final : public ViewProps {
    public:
    RTNTestViewProps() = default;
    RTNTestViewProps(const PropsParserContext& context, const RTNTestViewProps &sourceProps, const RawProps &rawProps);

      #pragma mark - Props
    
      std::string text{};
    

    };

小结

本节我们只是介绍了Fabric的组件以及渲染器的核心流程,在JS层定义UI布局,然后React层会构建一个DOM树,后面会将DOM树同步到ShadowNode层,最后会根据ShadowNode构建原生组件。
EmitterEvent,Props,State这些分发设置我们就不继续深入研究了,如果后续有机会的话可能会更加深入学习,目前我们只是了解一下跨端开发的原理和设计思想,拓宽一下眼界,下一节我们会学习一下Flutter,Flutter和RN的设计思想完全不同。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2228521.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DataSophon集成ApacheImpala的过程

注意: 本次安装操作系统环境为Anolis8.9(Centos7和Centos8应该也一样) DataSophon版本为DDP-1.2.1 整合的安装包我放网盘了: 通过网盘分享的文件&#xff1a;impala-4.4.1.tar.gz等2个文件 链接: https://pan.baidu.com/s/18KfkO_BEFa5gVcc16I-Yew?pwdza4k 提取码: za4k 1…

计算机网络-MSTP概述

一、RSTP/STP的缺陷与不足 前面我们学习了RSTP对于STP的一些优化与快速收敛机制。但在划分VLAN的网络中运行RSTP/STP&#xff0c;局域网内所有的VLAN共享一棵生成树&#xff0c;被阻塞后的链路将不承载任何流量&#xff0c;无法在VLAN间实现数据流量的负载均衡&#xff0c;导致…

字节青训-兔群繁殖之谜

问题描述 生物学家小 R 正在研究一种特殊的兔子品种的繁殖模式。这种兔子的繁殖遵循以下规律&#xff1a; 每对成年兔子每个月会生育一对新的小兔子&#xff08;一雌一雄&#xff09;。新生的小兔子需要一个月成长&#xff0c;到第二个月才能开始繁殖。兔子永远不会死亡。 小 R…

uniapp写移动端,适配苹果手机底部导航栏,ios安全区问题,苹果手机遮挡底部信息,uview的u-action-sheet组件

手机上有很多组件&#xff0c;需要手机底部弹窗来做选择,picker选择器&#xff0c;select列选择器呀这些&#xff0c;在苹果手机上会被底部nav遮住 采用了好几种配置的方式&#xff0c;多多少少都不太行&#xff0c;还是采用css来做吧&#xff0c;但是css来写想让它生效&#x…

从零开始使用Surya-OCR最新版本0.6.1——最强文本检测模型:新添表单表格检测识别

目录 一、更新概述 二、环境安装 1.基础环境配置 2.模型参数下载 3.参数地址配置——settings.py 三、指令使用 1.命令指令运行 一、更新概述 surya项目Github地址&#xff1a;https://github.com/VikParuchuri/surya 号称今年最强OCR的surya近期迎来新的更新&#xff0c;Vik…

深入理解C++ Lambda表达式:语法、用法与原理及其包装器的使用

深入理解C Lambda表达式&#xff1a;语法、用法与原理及其包装器的使用 lambda表达式C98中的一个例子lambda表达式语法lambda表达式各部分说明捕获列表说明 函数对象与lambda表达式 包装器function包装器 bind &#x1f30f;个人博客主页&#xff1a; 个人主页 本文深入介绍了…

nacos介绍

Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它致力于提供发现、配置和管理微服务的统一解决方案&#xff0c;以支持构建云原生应用。 服务发现&#xff08;Service Discovery&#xff09;&#xff1a; Nacos 支持服务的动态注册与发现&#xff…

【操作系统实验课】Git操作基础

1. Windows系统 1.1. Git下载安装 下载 Git 访问 Git 官方网站:https://git-scm.com/。 在页面中找到适合 Windows 系统的下载链接,一般会有 “Windows” 字样的按钮,点击下载安装程序。 安装 Git 运行下载的安装程序。 在安装向导中,一般可以选择默认设置,也可以根…

搜维尔科技:Xsens动作捕捉、Manus数据手套和Faceware面部捕捉技术集成,应用于元宇宙数字人制作解决方案

Xsens动作捕捉、Manus数据手套和Faceware面部捕捉技术集成&#xff0c;能够实现非常逼真且高效的数字人动作和表情捕捉&#xff01; 硬件连接与数据传输方面&#xff1a; 1.Xsens与Manus的集成&#xff1a;Xsens惯性动作捕捉系统通常可以与Manus的数据手套直接集成。Xsens主要…

MQTTnet4.3.x服务端+客户端实例测试(服务端和客户端方法及参数)

一、示例 目的&#xff1a;学习MQTTnet4.x使用方法&#xff0c;网上很多方法都是3.x版本介绍 二、方法调用 2.1 服务端 2.2 客户端 结合上篇博文&#xff0c;实现与多客户端进行交流&#xff08;实现在线客服功能&#xff09; 当然还有其他方法。之前曾写过相关MQTT文章&#…

【WebDriver】浏览器驱动下载及其配置

一、Windows电脑环境搭建-Chrome浏览器 行业内&#xff0c;Chrome (谷歌) 浏览器对于自动化程序来讲是比较稳定的. 自动化程序推荐使用 Chrome 浏览器的原因有几个&#xff1a; 开发者工具&#xff1a;Chrome 提供强大的开发者工具&#xff0c;方便调试和测试自动化脚本。 稳…

用jest做单元测试不得不知道的起手式配置,闭坑指南

做单元测试有很多的工具&#xff0c;今天在一个老项目中看到的用的工具是用的jest做的单元测试&#xff0c;特尝试更新下&#xff0c;遇到不少的问题。 相关依赖配置文件 npm install --save-dev jestpackage.json {"name": "jest-app","version&qu…

【Android】多渠道打包配置

目录 简介打包配置签名配置渠道配置配置打包出来的App名称正式包与测试包配置 打包方式开发工具打包命令行打包 优缺点 简介 多渠道打包 是指在打包一个 Android 应用时&#xff0c;一次编译生成多个 APK 文件&#xff0c;每个 APK 文件针对一个特定的渠道。不同的渠道可能代表…

Linux初学者导引:掌握主要命令与操作系统基础(第一天)

本文使用的工具&#xff1a;CentOS。 1.打开终端&#xff1a; 鼠标单击右键&#xff0c;选择“在终端打开(E)”选项。 2.命令行基础 常用命令&#xff1a; &#xff08;1&#xff09;ls:列出目录内容 列出当前目录或指定目录中的文件和文件夹。 基本用法&#xff1a;ls常…

块设备驱动的基本概念

块设备与字符设备 块设备只能以块为单位接收输入和返回输出&#xff0c;而字符设备则以字节为单位。大多数设备是字符设备&#xff0c;因为它们不需要缓冲而且不以固定块大小进行操作&#xff1b;字符设备只能被顺序读写&#xff0c;而块设备可以随机访问。 块设备对于I/O请求…

【力扣 + 牛客 | SQL题 | 每日4题】牛客大厂面试真题W3,W10

1. 牛客大厂面试真题SQLW3&#xff1a;分析客户逾期情况 1.1 题目&#xff1a; 描述 有贷款信息表&#xff1a;loan_tb&#xff08;agreement_id&#xff1a;合同id&#xff0c;customer_id&#xff1a;客户id&#xff0c;loan_amount&#xff1a;贷款金额&#xff0c;pay_a…

python 写web前端的库

Gradio vs Streamlit vs Dash vs Flask几款的对比 Gradio&#xff1a;Gradio 是专门为机器学习模型构建的。因此&#xff0c;如果您想专门为您构建的机器学习模型创建一个 Web UI&#xff0c;Gradio 的简单语法和设置是您的不二之选。 Streamlit&#xff1a;如果您想快速启动和…

Ubuntu系统安装软件

在Linux系统中有四种软件安装方式&#xff1a;rpm、yum、apt、编译安装 编译安装 编译安装只有一个源码包&#xff0c;源码包是由一大堆源代码程序组成的&#xff0c;是由程序员按照特定格式和语法编写好了&#xff0c;现成的安装包 程序&#xff1a;未执行的代码 进程&#…

Halcon 多相机统一坐标系(标定)

多相机统一坐标系是指将多个不同位置的相机的图像采集到同一个坐标系下进行处理和分析的方法。 在计算机视觉和机器视觉领域中&#xff0c;多相机统一坐标系被广泛应用于三维重建、立体视觉、目标跟踪等任务中。 以gen_binocular_rectification_map&#xff08;生成描述图像映…

【libGL error】Autodl云服务器配置ACT的conda虚拟环境生成训练数据时,遇到了libGL相关错误,涉及swrast_dri.so

问题与解决方案 1. libGL error: MESA-LOADER: failed to open iris conda install -c conda-forge libstdcxx-ng来源suffix _dri 下面的问题是在Autodl云服务器上运行程序是出现的&#xff0c;在Ubuntu笔记本上安装的Anaconda没有出现以下问题。 Autodl云服务器安装的是Mi…