1、React Native 源码分析(一)—— 启动流程
2、React Native 源码分析(二)—— 通信机制
3、React Native 源码分析(三)—— Native View创建流程
4、React Native 源码分析(四)—— 任务调度
5、React Native 源码分析(五)—— 事件分发
前两篇分析了,React Native 的启动和 通信机制,这篇来分析一下,在收到React 的通信数据字符串后,是如何在原生创建对应的View,React标签设置的View的属性,如何被原生View解析,为什么在自定义View调用requestLayout没有作用呢?
熟悉React 源码的小伙伴们应该知道,React对Component 最终解析为字符串,来提供给ReactNative 使用。在React 中是调用 UIManagerModule中提供的@ReactMethod 一系列方法,来实现在Android创建View。下面先来看下ReactNative中UI相关的类图、各类的功能。然后我们从Android接收到这些字符串开始分析源码。
图一(来源)
图二(来源)
在开始分析源码前,先来看下,从React 传递过来的数据是什么样的,如下图,在桥通信中,是根据methodId找到对应的mMethod(这里是UIManager.createView),后续根据 [3,"RCTRawText",31,{"text":"Press onChange"}]
来创建View
下面我们就从上图断点堆栈中的UIManagerModule 开始分析
源码分析 1、UIManagerModule# createView @ReactMethod
public void createView(int tag, String className, int rootViewTag, ReadableMap props) {
mUIImplementation.createView(tag, className, rootViewTag, props);
}
UIManagerModule 中函数的具体实现都是交给UIImplementation处理的,下面来看看在UIImplementation中处理
2、UIImplementation# createView /** Invoked by React to create a new node with a given tag, class name and properties. */
public void createView(int tag, String className, int rootViewTag, ReadableMap props) {
if (!mViewOperationsEnabled) {
return;
}
synchronized (uiImplementationThreadLock) {
//首先创建ShadowNode,它用来表示React中一个对应的node,之后根据这个ShadowNode去创建对应的View
ReactShadowNode cssNode = createShadowNode(className);
ReactShadowNode rootNode = mShadowNodeRegistry.getNode(rootViewTag);
//设置node的tag ,它的值是在React中生成的,
cssNode.setReactTag(tag); // Thread safety needed here
cssNode.setViewClassName(className);
cssNode.setRootTag(rootNode.getReactTag());
cssNode.setThemedContext(rootNode.getThemedContext());
mShadowNodeRegistry.addNode(cssNode);
//根据React中该node设置的属性,来更新对应的ReactShadowNode
ReactStylesDiffMap styles = null;
if (props != null) {
styles = new ReactStylesDiffMap(props);
//这里最终是调用了ReactShadowNode,@ReactProp注解方法的函数,把styles设置到ReactShadowNode的成员变量中的
cssNode.updateProperties(styles);
}
handleCreateView(cssNode, rootViewTag, styles);
}
}
下面介绍一下 该段代码的重要点,:
ReactShadowNodeReactShadowNode 代表一个React nodes,可以理解为Android层对React层view的表示,它存在的主要目的是 :
为了View的measure、layout。ReactNative的中原生View的measure、layout 是由Yoga这个库来实现的,而不是使用原生的measure、layout 。可以自定义属性,在React node中使用,例如public class ReactRawTextShadowNode extends ReactShadowNodeImpl {
@VisibleForTesting public static final String PROP_TEXT = "text";
private @Nullable String mText = null;
public ReactRawTextShadowNode() {}
@ReactProp(name = PROP_TEXT)
public void setText(@Nullable String text) {
mText = text;
markUpdated();
}
public @Nullable String getText() {
return mText;
}
@Override
public boolean isVirtual() {
return true;
}
@Override
public String toString() {
return getViewClass() + " [text: " + mText + "]";
}
}
ReactShadowNode、 YogaNode(java)、YogaNode(C++)是一一对应的,ReactNative 对View的测量、布局,是在Yoga框架中进行的,后面calculateRootLayout函数中会介绍
图片来自
ReactTag是在React中创建的,单数表示 默认UI框架 ,双数表示Fabric框架的node。两种ReactNative架构的对比,可以参考React Native 源码分析(一)—— 启动流程
在ReactFabric-dev.js 中的nextReactTag = 2
在点击事件中,也会用到ReactTag 用于区分新旧UI框架,如下图:
React Native 0.64.2 针对异常Unable to find JSIModule for class UIManager,怎么定位这个错误,顺着点击事件去找,在上面这个红框的位置断点,等待错误uiManagerType的出现,为什么会错误,是因为自定义view 中子view的tag,没有按照ReactTag的分配规则
cssNode.updateProperties(styles);ReactShadowNodeImpl 中的 updateProperties
,会调用到ViewManagerPropertyUpdater.updateProps
,如下
public static <T extends ReactShadowNode> void updateProps(T node, ReactStylesDiffMap props) {
//查找ShadowNodeSetter,该接口有setProperty和getProperties 两个方法
ShadowNodeSetter<T> setter = findNodeSetter(node.getClass());
Iterator<Map.Entry<String, Object>> iterator = props.mBackingMap.getEntryIterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> entry = iterator.next();
//设置属性值到ReactShadowNode中
setter.setProperty(node, entry.getKey(), entry.getValue());
}
}
上面的实现比较抽象,具体ShadowNodeSetter的实现是什么,来看下面的代码
private static <T extends ReactShadowNode> ShadowNodeSetter<T> findNodeSetter(
Class<? extends ReactShadowNode> nodeClass) {
@SuppressWarnings("unchecked")
//在缓存中查找
ShadowNodeSetter<T> setter = (ShadowNodeSetter<T>) SHADOW_NODE_SETTER_MAP.get(nodeClass);
if (setter == null) {
//查找是否有 Class.forName(clsName + "$$PropsSetter"); 的类,如果没有提供就使用FallbackShadowNodeSetter
//我们在自定义ReactShadowNode时,也可以写个内部类PropsSetter,来实现属性赋值的 *** 作
setter = findGeneratedSetter(nodeClass);
if (setter == null) {
setter = new FallbackShadowNodeSetter<>(nodeClass);
}
SHADOW_NODE_SETTER_MAP.put(nodeClass, setter);
}
return setter;
}
如果没有提供PropsSetter,那么都会使用默认的FallbackShadowNodeSetter,下面来看看
private FallbackShadowNodeSetter(Class<? extends ReactShadowNode> shadowNodeClass) {
//它会根据ReactShadowNode,把其中使用@ReactProp 注解的方法提取出来,用注解属性name的值 进行区分
mPropSetters =
ViewManagersPropertyCache.getNativePropSettersForShadowNodeClass(shadowNodeClass);
}
@Override
public void setProperty(ReactShadowNode node, String name, Object value) {
//获取注解属性name的值 对应的函数,函数时以Method 的形式存储在类PropSetter中
ViewManagersPropertyCache.PropSetter setter = mPropSetters.get(name);
if (setter != null) {
//通过反射,在对象node上调用函数,传值为value,这样就相当于是调用了ReactShadowNode中@ReactProp 注解的方法
setter.updateShadowNodeProp(node, value);
}
}
到此ReactShadowNode 就已经创建、赋值完成了,继续往下分析handleCreateView
3、UIImplementation# handleCreateView protected void handleCreateView(
ReactShadowNode cssNode, int rootViewTag, @Nullable ReactStylesDiffMap styles) {
//是否是一个虚拟的node,就是没有对应的native view。
// 那么有个疑问,没有对应的native view,那它的内容如何显示出来呢?
if (!cssNode.isVirtual()) {
mNativeViewHierarchyOptimizer.handleCreateView(cssNode, cssNode.getThemedContext(), styles);
}
}
通过图一,可以清楚看到,UIImplementation中的函数,都交给了NativeViewHierarchyOptimizer,对View 的层级进行优化
4、NativeViewHierarchyOptimizer# handleCreateView /** Handles a createView call. May or may not actually create a native view. */
public void handleCreateView(
ReactShadowNode node,
ThemedReactContext themedContext,
@Nullable ReactStylesDiffMap initialProps) {
//判断这个View 是否是一个纯容器view,它自身没有任何需要绘制的,只是为了包裹其他view
boolean isLayoutOnly =
node.getViewClass().equals(com.facebook.react.uimanager.ViewProps.VIEW_CLASS_NAME)
&& isLayoutOnlyAndCollapsable(initialProps);
node.setIsLayoutOnly(isLayoutOnly);
if (node.getNativeKind() != NativeKind.NONE) {
//向队列中添加一个创建view的任务
mUIViewOperationQueue.enqueueCreateView(
themedContext, node.getReactTag(), node.getViewClass(), initialProps);
}
}
5、UIViewOperationQueue# enqueueCreateView
public void enqueueCreateView(
ThemedReactContext themedContext,
int viewReactTag,
String viewClassName,
@Nullable ReactStylesDiffMap initialProps) {
synchronized (mNonBatchedOperationsLock) {
mCreateViewCount++;
//添加到mNonBatchedOperations ArrayDeque
mNonBatchedOperations.addLast(
new CreateViewOperation(themedContext, viewReactTag, viewClassName, initialProps));
}
}
代码5.2
private final class CreateViewOperation extends ViewOperation {
private final ThemedReactContext mThemedContext;
private final String mClassName;
private final @Nullable ReactStylesDiffMap mInitialProps;
public CreateViewOperation(
ThemedReactContext themedContext,
int tag,
String className,
@Nullable ReactStylesDiffMap initialProps) {
super(tag);
mThemedContext = themedContext;
mClassName = className;
mInitialProps = initialProps;
Systrace.startAsyncFlow(Systrace.TRACE_TAG_REACT_VIEW, "createView", mTag);
}
@Override
public void execute() {
//执行队列的任务时,会执行这里
Systrace.endAsyncFlow(Systrace.TRACE_TAG_REACT_VIEW, "createView", mTag);
mNativeViewHierarchyManager.createView(mThemedContext, mTag, mClassName, mInitialProps);
}
}
创建view的 *** 作,被添加到mNonBatchedOperations后,在js->java 一批通信结束后,会调用这些 *** 作,下面来看看
6、onBatchComplete通过前两篇文章,我们知道,js->java 的回调是 CatalystInstanceImpl 中的静态内部类BridgeCallback,onBatchComplete调用过程如下
BridgeCallback#onBatchComplete -> NativeModuleRegistry#onBatchComplete -> UIManagerModule#onBatchComplete
UIManagerModule#onBatchComplete
为了省篇幅,就不贴代码了,执行 UIManagerModule中的onBatchComplete ,如果设置listener,就调用。最后调用到mUIImplementation.dispatchViewUpdates(batchId);
/** Invoked at the end of the transaction to commit any updates to the node hierarchy. */
public void dispatchViewUpdates(int batchId) {
...
final long commitStartTime = SystemClock.uptimeMillis();
try {
//更新Root及其子View 的大小、位置。子View的更新是添加到队列中的,代码7分析
updateViewHierarchy();
//在上个函数更新View的时候,会mTagsWithLayoutVisited.put(tag, true); 防止相同的更新被添加到队列。添加队列完成后,就清除mTagsWithLayoutVisited
mNativeViewHierarchyOptimizer.onBatchComplete();
//从队列中取出任务,添加到新线程中,等ReactChoreographer执行回调mDispatchUIFrameCallback时,会触发新线程中的任务,代码8分析
mOperationsQueue.dispatchViewUpdates(batchId, commitStartTime, mLastCalculateLayoutTime);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
7、UIImplementation #updateViewHierarchy
protected void updateViewHierarchy() {
try {
for (int i = 0; i < mShadowNodeRegistry.getRootNodeCount(); i++) {
int tag = mShadowNodeRegistry.getRootTag(i);
ReactShadowNode cssRoot = mShadowNodeRegistry.getNode(tag);
//root view 的widthSpec、heightSpec 是 在ReactRootView中通过uiManager.updateRootLayoutSpecs设置的
if (cssRoot.getWidthMeasureSpec() != null && cssRoot.getHeightMeasureSpec() != null) {
try {
//递归通知所有cssNode 即将开始calculateLayout
notifyOnBeforeLayoutRecursive(cssRoot);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
//计算root view 的位置,通过YogaNative 来计算的
calculateRootLayout(cssRoot);
try {
//递归更新子View,其中如果hasNewLayout()为true,就把更新子View的任务加入到队列UIViewOperationQueue
//然后设置标志位mHasNewLayout = false,后面在访问相同cssNode的 hasNewLayout()就是false
applyUpdatesRecursive(cssRoot, 0f, 0f);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
...
}
}
} ...
}
下面分析一下其中的 calculateRootLayout和applyUpdatesRecursive
calculateRootLayoutcalculateRootLayout(cssRoot);的调用过程如下:
ReactShadowNode 和 YogaNode是一一对应的
ReactShadowNode的实现是ReactShadowNodeImpl,YogaNode的实现有YogaNodeJNIBase
代码7.2
calculateRootLayout(cssRoot); //cssRoot 是根ReactShadowNode
-> ReactShadowNode# calculateLayout
-> YogaNodeJNIBase# calculateLayout(width, height); //广度优先遍历YogaNode节点
->YogaNative.jni_YGNodeCalculateLayoutJNI(mNativePointer, width, height, nativePointers, nodes);
//下面调用到C++,源码路径ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNIVanilla.cpp
->jni_YGNodeCalculateLayoutJNI
->YGNodeCalculateLayoutWithContext
->YGLayoutNodeInternal //这里会先从缓存中,取数据,如果没有就进行计算
->YGNodelayoutImpl // 这里就是RN 的 flexbox layout 算法的实现,
可以通过ReactShadowNode#setMeasureFunction() 来向YogaNode设置一个测量函数,在Yoga框架中 对UI进行计算时,就可以调用到我们自定义的Measure函数
举个例子:
ReactTextViewManager 中创建
public ReactTextShadowNode createShadowNodeInstance() {
return new ReactTextShadowNode();
}
ReactTextShadowNode 的是继承ReactBaseTextShadowNode
构造函数会调用initMeasureFunction->setMeasureFunction,后者的实现是在ReactBaseTextShadowNode
@Override
public void setMeasureFunction(YogaMeasureFunction measureFunction) {
mYogaNode.setMeasureFunction(measureFunction);
}
mYogaNode对应的类是 YogaNodeJNIBase
public void setMeasureFunction(YogaMeasureFunction measureFunction) {
//最终还是在java中调用自定义的measure
mMeasureFunction = measureFunction;
//告诉YogaNative 有自定义的measure
YogaNative.jni_YGNodeSetHasMeasureFuncJNI(mNativePointer, measureFunction != null);
}
jni_YGNodeSetHasMeasureFuncJNI 调用到C++ src/main/jni/first-party/yogajni/jni/YGJNIVanilla.cpp
static void jni_YGNodeSetHasMeasureFuncJNI(
JNIEnv* env,
jobject obj,
jlong nativePointer,
jboolean hasMeasureFunc) {
//调用到node_modules/react-native/ReactCommon/yoga/yoga/YGNode.cpp 的setMeasureFunc
_jlong2YGNodeRef(nativePointer)
->setMeasureFunc(hasMeasureFunc ? YGJNIMeasureFunc : nullptr);
}
在YGNode.cpp 中,是把函数YGJNIMeasureFunc 设置到了measure_ 的YGMeasureFunc类型变量 noContext 中了,YGJNIMeasureFunc在下面会介绍到。
接着代码7.2 看看,在YGNodelayoutImpl 算法中,是如何调用到设置的measureFunction
->YGNodelayoutImpl
//hasMeasureFunc 就是判断measure_.noContext 是否为空
-> if (node->hasMeasureFunc()) {
YGNodeWithMeasureFuncSetMeasuredDimensions
}
-> const YGSize measuredSize = node->measure(
innerWidth,
widthMeasureMode,
innerHeight,
heightMeasureMode,
layoutContext);
//进入类 node_modules/react-native/ReactCommon/yoga/yoga/YGNode.cpp,见下图7.1
-> YGSize YGNode::measure(
float width,
YGMeasureMode widthMode,
float height,
YGMeasureMode heightMode,
void* layoutContext) {
return facebook::yoga::detail::getBooleanData(flags, measureUsesContext_)
? measure_.withContext(
this, width, widthMode, height, heightMode, layoutContext)
: measure_.noContext(this, width, widthMode, height, heightMode);
}
//进入类 ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNIVanilla.cpp 中的YGJNIMeasureFunc,
//是在jni_YGNodeSetHasMeasureFuncJNI中设置的
//调用到java YogaNodeJNIBase 的measure函数,因为objectClass是YogaNodeJNIBase的对象
//见下图7.2
-> static const jmethodID methodId = facebook::yoga::vanillajni::getMethodId(
env, objectClass.get(), "measure", "(FIFI)J");
下图7.1
下图7.2
// Implementation Note: Why this method needs to stay final
//
// We cache the jmethodid for this method in Yoga code. This means that even if a subclass
// were to override measure, we'd still call this implementation from layout code since the
// overriding method will have a different jmethodid. This is final to prevent that mistake.
@DoNotStrip
public final long measure(float width, int widthMode, float height, int heightMode) {
if (!isMeasureDefined()) {
throw new RuntimeException("Measure function isn't defined!");
}
//这里就调用到了自定义的measure
return mMeasureFunction.measure(
this,
width,
YogaMeasureMode.fromInt(widthMode),
height,
YogaMeasureMode.fromInt(heightMode));
}
最终每个YogaNode 测量、布局完的结果,会保存到YogaNodeJNIBase的 arr 数组中
applyUpdatesRecursive(cssRoot, 0f, 0f);的调用过程如下:UIImplementation#applyUpdatesRecursive()
-> ReactShadowNodeImpl#.dispatchUpdates() //把YogaNode arr 数组中的布局结果,赋给ReactShadowNode对应的值
-> UIViewOperationQueue#.enqueueUpdateLayout() //在hasNewLayout() 检查mHasNewLayout ,如果通过 则nativeViewHierarchyOptimizer.handleUpdateLayout,在队列中添加一条更新布局的任务UpdateLayoutOperation
-> ReactShadowNodeImpl#.markUpdateSeen() //设置标志位mHasNewLayout = false
上面代码enqueueUpdateLayout,添加任务UpdateLayoutOperation 到 UIViewOperationQueue 的 mOperations中
代码7.10
public UpdateLayoutOperation(int parentTag, int tag, int x, int y, int width, int height) {
super(tag);
mParentTag = parentTag;
mX = x;
mY = y;
mWidth = width;
mHeight = height;
Systrace.startAsyncFlow(Systrace.TRACE_TAG_REACT_VIEW, "updateLayout", mTag);
}
@Override
public void execute() {
Systrace.endAsyncFlow(Systrace.TRACE_TAG_REACT_VIEW, "updateLayout", mTag);
//这个任务的执行,在运行任务时,再进行分析
mNativeViewHierarchyManager.updateLayout(mParentTag, mTag, mX, mY, mWidth, mHeight);
}
UpdateLayoutOperation任务的执行,在运行任务时,再进行分析。下面继续代码6分析 mOperationsQueue.dispatchViewUpdates(batchId, commitStartTime, mLastCalculateLayoutTime);
public void dispatchViewUpdates(
final int batchId, final long commitStartTime, final long layoutTime) {
...省略部分代码...
try {
final long dispatchViewUpdatesTime = SystemClock.uptimeMillis();
final long nativeModulesThreadCpuTime = SystemClock.currentThreadTimeMillis();
// Store the current operation queues to dispatch and create new empty ones to continue
// receiving new operations
final ArrayList<DispatchCommandViewOperation> viewCommandOperations;
if (!mViewCommandOperations.isEmpty()) {
viewCommandOperations = mViewCommandOperations;
mViewCommandOperations = new ArrayList<>();
} else {
viewCommandOperations = null;
}
final ArrayList<UIOperation> batchedOperations;
if (!mOperations.isEmpty()) {
batchedOperations = mOperations;
mOperations = new ArrayList<>();
} else {
batchedOperations = null;
}
final ArrayDeque<UIOperation> nonBatchedOperations;
synchronized (mNonBatchedOperationsLock) {
if (!mNonBatchedOperations.isEmpty()) {
nonBatchedOperations = mNonBatchedOperations;
mNonBatchedOperations = new ArrayDeque<>();
} else {
nonBatchedOperations = null;
}
}
if (mViewHierarchyUpdateDebugListener != null) {
mViewHierarchyUpdateDebugListener.onViewHierarchyUpdateEnqueued();
}
//创建runOperations,执行viewCommandOperations、nonBatchedOperations和batchedOperations 中的任务
Runnable runOperations =
new Runnable() {
@Override
public void run() {
...省略部分代码...
try {
long runStartTime = SystemClock.uptimeMillis();
// All ViewCommands should be executed first as a perf optimization.
// This entire block is only executed if there's at least one ViewCommand queued.
if (viewCommandOperations != null) {
for (DispatchCommandViewOperation op : viewCommandOperations) {
try {
op.executeWithExceptions();
} catch (RetryableMountingLayerException e) {
...省略部分代码...
}
}
}
// All nonBatchedOperations should be executed before regular operations as
// regular operations may depend on them
if (nonBatchedOperations != null) {
for (UIOperation op : nonBatchedOperations) {
op.execute();
}
}
if (batchedOperations != null) {
for (UIOperation op : batchedOperations) {
op.execute();
}
}
...省略部分代码...
// Clear layout animation, as animation only apply to current UI operations batch.
mNativeViewHierarchyManager.clearLayoutAnimation();
if (mViewHierarchyUpdateDebugListener != null) {
mViewHierarchyUpdateDebugListener.onViewHierarchyUpdateFinished();
}
} catch (Exception e) {
mIsInIllegalUIState = true;
throw e;
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
};
...省略部分代码...
//把上面的runable加入到mDispatchUIRunnables中,mDispatchUIRunnables 会在 doFrameGuarded中flushPendingBatches 执行,代码9介绍
synchronized (mDispatchRunnablesLock) {
mDispatchUIRunnables.add(runOperations);
}
// In the case where the frame callback isn't enqueued, the UI isn't being displayed or is
// being
// destroyed. In this case it's no longer important to align to frames, but it is important to
// make
// sure any late-arriving UI commands are executed.
if (!mIsDispatchUIFrameCallbackEnqueued) {
UiThreadUtil.runOnUiThread(
new GuardedRunnable(mReactApplicationContext) {
@Override
public void runGuarded() {
flushPendingBatches();
}
});
}
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
viewCommandOperations 执行 JS层View 调用的函数(命令),最终会调用到Java层的ViewManager中receiveCommand函数中nonBatchedOperations 只有创建View 的任务CreateViewOperation,会被添加到这个队列中batchedOperations 除了创建View的任务,例如 更新布局UpdateLayoutOperation 等等。
这里之所以要把创建View,和其他View *** 作的任务 分开,是因为对View *** 作的任务,需要先创建出View后,才能进行。所以nonBatchedOperations必须要先于batchedOperations执行
接下来,我们来看加入的这些任务,是在什么时候被调用的。这部分代码比较重要,源码中自带的UI JS 帧率监测,也是在这个时机
9、DispatchUIFrameCallbackDispatchUIFrameCallback继承了Choreographer,看到后者大家应该清楚了,是在每一帧的回调执行任务,没错。回调注册和删除是在Activity的resume和pause时
/* package */ void resumeFrameCallback() {
mIsDispatchUIFrameCallbackEnqueued = true;
ReactChoreographer.getInstance()
.postFrameCallback(ReactChoreographer.CallbackType.DISPATCH_UI, mDispatchUIFrameCallback);
}
/* package */ void pauseFrameCallback() {
mIsDispatchUIFrameCallbackEnqueued = false;
ReactChoreographer.getInstance()
.removeFrameCallback(ReactChoreographer.CallbackType.DISPATCH_UI, mDispatchUIFrameCallback);
flushPendingBatches();
}
Choreographer的重要回调doFrame,最后会调用DispatchUIFrameCallback 的doFrameGuarded
@Override
public void doFrameGuarded(long frameTimeNanos) {
...
try {
//先执行nonBatchedOperations中的任务,都是创建View的
dispatchPendingNonBatchedOperations(frameTimeNanos);
} ...
//执行代码8,加入的Runable。执行viewCommandOperations、nonBatchedOperations、batchedOperations
flushPendingBatches();
ReactChoreographer.getInstance()
.postFrameCallback(ReactChoreographer.CallbackType.DISPATCH_UI, this);
}
下面接代码5.2,来看一下任务CreateViewOperation的执行mNativeViewHierarchyManager.createView(mThemedContext, mTag, mClassName, mInitialProps)
根据ViewManager,来创建View。一般我们自定义View给JS层使用,是需要自定义ViewManager
public synchronized void createView(
ThemedReactContext themedContext,
int tag,
String className,
@Nullable ReactStylesDiffMap initialProps) {
UiThreadUtil.assertOnUiThread();
try {
//获取到对应的ViewManager
ViewManager viewManager = mViewManagers.get(className);
//如果是自定义View,需要实现ViewManager中的createView方法
View view =
viewManager.createView(tag, themedContext, initialProps, null, mJSResponderHandler);
mTagsToViews.put(tag, view);
mTagsToViewManagers.put(tag, viewManager);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_VIEW);
}
}
mViewManagers 中的数据,是在React Native初始化的时候,进行收集的(可参考文章React Native 源码分析(一)—— 启动流程),通过UIManagerModule来设置到NativeViewHierarchyManager的ViewManagerRegistry类型的mViewManagers变量中
大部分自定义ViewManager都是继承SimpleViewManager、ViewGroupManager
介绍一些自定义ViewManager的一些有用方法,最基本的就不介绍了:
onDropViewInstance 删除view时,会触发该方法,可以进行一些清理工作onAfterUpdateTransaction 在属性都初始化完成之后,会调用该方法receiveCommand 可以在js层通过View直接调用该命令,而不用 通过ReactContextBaseJavaModule到此,从React到Android, View的创建过程就分析完了。通过上面分析知道,View的measure和layout,是通过Yoga进行的,那么layout的结果,该如何使用呢?下面接代码代码7.10,来看一下任务UpdateLayoutOperation的执行mNativeViewHierarchyManager.updateLayout(mParentTag, mTag, mX, mY, mWidth, mHeight);
public synchronized void updateLayout(
int parentTag, int tag, int x, int y, int width, int height) {
...省略部分代码...
try {
View viewToUpdate = resolveView(tag);
...省略部分代码...
// Check if the parent of the view has to layout the view, or the child has to lay itself out.
//判断是否是root view
if (!mRootTags.get(parentTag)) {
//获取一下,即将layout view 的父view 对应的ViewManager,目的就是为了判断needsCustomLayoutForChildren()的返回值
ViewManager parentViewManager = mTagsToViewManagers.get(parentTag);
IViewManagerWithChildren parentViewManagerWithChildren;
if (parentViewManager instanceof IViewManagerWithChildren) {
parentViewManagerWithChildren = (IViewManagerWithChildren) parentViewManager;
} else {
throw new IllegalViewOperationException(
"Trying to use view with tag "
+ parentTag
+ " as a parent, but its Manager doesn't implement IViewManagerWithChildren");
}
//needsCustomLayoutForChildren 返回 true 表示子view的layout由android原生的来调用,false 表示由RN 来设置layout的参数
if (parentViewManagerWithChildren != null
&& !parentViewManagerWithChildren.needsCustomLayoutForChildren()) {
//该函数里面 调用view 的layout,把 Yoga的测量结果,告知到Android 体系中
updateLayout(viewToUpdate, x, y, width, height);
}
} else {
updateLayout(viewToUpdate, x, y, width, height);
}
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_VIEW);
}
}
最后看一下updateLayout
代码11.2
private void updateLayout(View viewToUpdate, int x, int y, int width, int height) {
//
if (mLayoutAnimationEnabled && mLayoutAnimator.shouldAnimateLayout(viewToUpdate)) {
//如果允许动画的话,在这里处理
mLayoutAnimator.applyLayoutUpdate(viewToUpdate, x, y, width, height);
} else {
//一般到这里,layout就是android的三大绘制函数之一
viewToUpdate.layout(x, y, x + width, y + height);
}
}
主要的流程就全部分析完了,那看到这里,我们来思考个问题:
React 中的View,它们的UI style 使用的是css那一套。那么ReactNative 使用Yoga在YGNodelayoutImpl 实现对UI 、布局的解析,可以得到每个View的宽高和位置,也就是代替了在android中的measure、layout功能 ,最终的结果也是需要告知到Android体系中,也就是代码11.2 调用了layout。 正常情况下在android中的ViewGroup 的 layout 会调用所有嵌套View的layout,那岂不是和ReactNative 使用Yoga 的出现矛盾了?(不用担心View的layout,因为代码11.2 调用 View的layout,只是设置自身的位置,不会调用到其他View的layout)
在React中所有的node都会被包裹在
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// No-op since UIManagerModule handles actually laying out children.
}
public void requestLayout() {
// No-op, terminate `requestLayout` here, UIManagerModule handles laying out children and
// `layout` is called on all RN-managed views by `NativeViewHierarchyManager`
}
可是看到,重写了onLayout和requestLayout,但都是空实现。onLayout 没有必要 也不能实现,因为子View的位置都已经在Yoga 中确定了。下面的子View调用requestLayout,最终也会在这里被打断,因为measure、layout都在Yoga中计算了,requestLayout 也就形同虚设了。
到此Android层面的UI流程就大概分析完了,虽然不是逐行代码分析,但比网上大大部分,要详细很多。欢迎小伙伴们 点赞、评论 支持一下,U·ェ·U
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)