当前分析的ReactNative版本为0.61.5:
看这边文章之前最好先了解UIManagerModule和UIImplementation:
1.ReactNative源码分析之UIManagerModule.
2.ReactNative源码分析之UIImplementation.
从UIViewOperationQueue名称可知,首先该类是一个操作UIView为主,同时具有缓存功能的这样一个类。
看下这个类的定义:
/**
* This class acts as a buffer for command executed on {@link NativeViewHierarchyManager}. It expose
* similar methods as mentioned classes but instead of executing commands immediately it enqueues
* those operations in a queue that is then flushed from {@link UIManagerModule} once JS batch of ui
* operations is finished. This is to make sure that we execute all the JS operation coming from a
* single batch a single loop of the main (UI) android looper.
*
* <p>TODO(7135923): Pooling of operation objects TODO(5694019): Consider a better data structure
* for operations queue to save on allocations
*/
public class UIViewOperationQueue {
}
该类扮演buffer的角色,接收command命令,这些命令会在NativeViewHierarchyManager中执行。
这些command并不会立即执行,它首先会进入缓存队列,所有command会在UIManagerModule执行完JS batch后再执行。保证了一批js命令执行后,缓存中的UI command也能一次性执行完。
一、UIOperation和ViewOperation
ViewOperation实现UIOperation接口,既然是对相关View的操作,需要传入相关View的tag。
举例看下具体实现:
private final class RemoveRootViewOperation extends ViewOperation {
public RemoveRootViewOperation(int tag) {
super(tag);
}
@Override
public void execute() {
mNativeViewHierarchyManager.removeRootView(mTag);
}
}
这个是删除RootView的UI操作,本质上是由NativeViewHierarchyManager代理执行。
再来看看UpdatePropertiesOperation定义:
private final class UpdatePropertiesOperation extends ViewOperation {
private final ReactStylesDiffMap mProps;
private UpdatePropertiesOperation(int tag, ReactStylesDiffMap props) {
super(tag);
mProps = props;
}
@Override
public void execute() {
mNativeViewHierarchyManager.updateProperties(mTag, mProps);
}
}
也是类似,真正是由NativeViewHierarchyManager代理执行。
定义的UIViewOperation都有哪些呢?如下:
1.ChangeJSResponderOperation;
2.ConfigureLayoutAnimationOperation;
3.CreateViewOperation;
4.DismissPopupMenuOperation;
5.DispatchStringCommandOperation;
6.FindTargetForTouchOperation;
7.LayoutUpdateFinishedOperation;
8.ManageChildrenOperation;
9.MeasureOperation;
10.MeasureInWindowOperation;
11.EmitOnLayoutEventOperation;
12.RemoveRootViewOperation;
13.SendAccessibilityEvent;
14.SetChildrenOperation;
15.SetLayoutAnimationEnabledOperation;
16.ShowPopupMenuOperation;
17.UIBlockOperation;
18.UpdateViewExtraData;
19.UpdateInstanceHandleOperation;
20.UpdateLayoutOperation;
21.UpdatePropertiesOperation;
所有的都是对UI的操作,最终是由NativeViewHierarchyManager执行。
二、dispatchViewUpdates
如上分析可知,所有的UI操作都会进入到缓存队列,那么什么时候批量执行呢?
dispatchViewUpdates会在onBatchComplete方法进行触发,onBatchComplete方法后续会进行分析。
看该方法之前,先来看看DispatchUIFrameCallback定义:
/**
* Choreographer FrameCallback responsible for actually dispatching view updates on the UI thread
* that were enqueued via {@link #dispatchViewUpdates(int)}. The reason we don't just enqueue
* directly to the UI thread from that method is to make sure our Runnables actually run before
* the next traversals happen:
*
* <p>ViewRootImpl#scheduleTraversals (which is called from invalidate, requestLayout, etc) calls
* Looper#postSyncBarrier which keeps any UI thread looper messages from being processed until
* that barrier is removed during the next traversal. That means, depending on when we get updates
* from JS and what else is happening on the UI thread, we can sometimes try to post this runnable
* after ViewRootImpl has posted a barrier.
*
* <p>Using a Choreographer callback (which runs immediately before traversals), we guarantee we
* run before the next traversal.
*/
private class DispatchUIFrameCallback extends GuardedFrameCallback {
private static final int FRAME_TIME_MS = 16;
private final int mMinTimeLeftInFrameForNonBatchedOperationMs;
private DispatchUIFrameCallback(
ReactContext reactContext, int minTimeLeftInFrameForNonBatchedOperationMs) {
super(reactContext);
mMinTimeLeftInFrameForNonBatchedOperationMs = minTimeLeftInFrameForNonBatchedOperationMs;
}
@Override
public void doFrameGuarded(long frameTimeNanos) {
if (mIsInIllegalUIState) {
FLog.w(
ReactConstants.TAG,
"Not flushing pending UI operations because of previously thrown Exception");
return;
}
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "dispatchNonBatchedUIOperations");
try {
dispatchPendingNonBatchedOperations(frameTimeNanos);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);