React-Native-ソース分析2-JSXがオリジナルページにどのようにレンダリングされるか(上)

14638 ワード

本文はReact-Nativeの通信過程をスキップして、詳しくは大頭鬼が書いたJavaとJSの通信原理を参考にしてください.0.33バージョンには怠惰なロードが入っていますが、元の構成表の生成のタイミングと方式が変わりましたが、原理は変わりません.約束のJSONを通じて、moduleName、function nameを解釈して、それからローカルで対応するモジュールの中の方法を見つけて、これらの方法は、反射によって実行され、呼び出しが実現される.
この記事では、Android原生からJSXがどのように最終的に原生コントロールに変化するかを反転します.
ブロガーが使用する環境は(バージョンが重要で、RNの発展が速く、異なるバージョンの間に差がある可能性があります)
“react”: “15.3.1”,
“react-native”: “^0.33.0”,
React-Nativeソースコード分析-JSページを起動する方法の最後のステップでは、XReactInstanceManagerImplを参照してください.JAvaのattachMeasuredRootView ToInstanceメソッドにはViewを設定するロジックがあります
  private void attachMeasuredRootViewToInstance(
    ...
    UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
    int rootTag = uiManagerModule.addMeasuredRootView(rootView);
    rootView.setRootViewTag(rootTag);
    ...
  }

uiManagerModuleが見えます.addMeasuredRootView(rootView)という方法がすごいようなので入ってみます
public int addMeasuredRootView(final SizeMonitoringFrameLayout rootView) {
    //          

    mUIImplementation.registerRootView(rootView, tag, width, height, themedRootContext);
    //   setOnSizeChangedListener

    return tag;
  }

関係のないコードを削除すると、mUIImplementationが表示されます.registerRootView(rootView,tag,width,height,themedRootContext)メソッドはviewと相関幅,高さ,theme情報を伝達し,コードを見てこれらのデータを利用してReactShadowNodeを構築したことを発見し,addがmOperationsQueueに到着すると,Queueを見るとUI関連のループがUI描画トランザクションを処理していることをすぐに思い出した.
public void registerRootView(
      SizeMonitoringFrameLayout rootView,
      int tag,
      int width,
      int height,
      ThemedReactContext context) {
    final ReactShadowNode rootCSSNode = createRootShadowNode();
    rootCSSNode.setReactTag(tag);
    rootCSSNode.setThemedContext(context);
    rootCSSNode.setStyleWidth(width);
    rootCSSNode.setStyleHeight(height);

    mShadowNodeRegistry.addRootNode(rootCSSNode);

    // register it within NativeViewHierarchyManager
    mOperationsQueue.addRootView(tag, rootView, context);
  }

まずここを置いて、前の方法addMeasuredRootViewの注釈に戻ります.
/**
   * Registers a new root view. JS can use the returned tag with manageChildren to add/remove
   * children to this view.
   *
   * Note that this must be called after getWidth()/getHeight() actually return something. See
   * CatalystApplicationFragment as an example.
   *
   * TODO(6242243): Make addMeasuredRootView thread safe
   * NB: this method is horribly not-thread-safe.
   */

jsはtagに基づいてmanageChildrenを使用してrootviewのサブviewを追加、削除することができます.
ではmanageChildrenはjsが生コードの削除レイアウトを直接制御するエントリである可能性があると推測できます.
@ReactMethod
  public void manageChildren(
      int viewTag,
      @Nullable ReadableArray moveFrom,
      @Nullable ReadableArray moveTo,
      @Nullable ReadableArray addChildTags,
      @Nullable ReadableArray addAtIndices,
      @Nullable ReadableArray removeFrom) {

    mUIImplementation.manageChildren(
        viewTag,
        moveFrom,
        moveTo,
        addChildTags,
        addAtIndices,
        removeFrom);
  }

やはりこれはReactMethodで注釈した方法で、これを代表してJSに直接呼び出されます.注釈:Interface for adding/removing/moving views within a parent view from JSからもjsがこの方法でviewを削除して変更することがわかります.同じ@ReactMethod注釈のあるクラスには、createView、removeRootView、updateView、setChildren、replaceExistingNonRootView、removeSubviewsFromContainerWithID,measure,measureInWindow...ちょっと待ってcreateViewのような方法を探してみました
   @ReactMethod
  public void createView(int tag, String className, int rootViewTag, ReadableMap props) {
    if (DEBUG) {
      FLog.d(
          ReactConstants.TAG,
          "(UIManager.createView) tag: " + tag + ", class: " + className + ", props: " + props);
    }
    mUIImplementation.createView(tag, className, rootViewTag, props);
  }

mUIImplementationに進みます.createView,
 public void createView(int tag, String className, int rootViewTag, ReadableMap props) {
    ReactShadowNode cssNode = createShadowNode(className);
    ReactShadowNode rootNode = mShadowNodeRegistry.getNode(rootViewTag);
    cssNode.setReactTag(tag);
    cssNode.setViewClassName(className);
    cssNode.setRootNode(rootNode);
    cssNode.setThemedContext(rootNode.getThemedContext());

    mShadowNodeRegistry.addNode(cssNode);

    ReactStylesDiffMap styles = null;
    if (props != null) {
      styles = new ReactStylesDiffMap(props);
      cssNode.updateProperties(styles);
    }

    handleCreateView(cssNode, rootViewTag, styles);
  }

ReactShadowNodeを作成します.createShadowNodeはclassNameを使用して、ReactTextInputManagerなどの以前に登録されていたViewManagerを見つけ、rootNodeを設定し、最後にhandleCreateViewを設定します.
  protected void handleCreateView(
      ReactShadowNode cssNode,
      int rootViewTag,
      @Nullable ReactStylesDiffMap styles) {
    if (!cssNode.isVirtual()) {
      mNativeViewHierarchyOptimizer.handleCreateView(cssNode, cssNode.getThemedContext(), styles);
    }
  }

  public void handleCreateView(
      ReactShadowNode node,
      ThemedReactContext themedContext,
      @Nullable ReactStylesDiffMap initialProps) {
    if (!ENABLED) {
      int tag = node.getReactTag();
      mUIViewOperationQueue.enqueueCreateView(
          themedContext,
          tag,
          node.getViewClass(),
          initialProps);
      return;
    }
  }

public void enqueueCreateView(
      ThemedReactContext themedContext,
      int viewReactTag,
      String viewClassName,
      @Nullable ReactStylesDiffMap initialProps) {
    synchronized (mNonBatchedOperationsLock) {
      mNonBatchedOperations.addLast(
        new CreateViewOperation(
          themedContext,
          viewReactTag,
          viewClassName,
          initialProps));
    }
  }

このように続けていくと、createViewの1つのViewを作成するには、最後にArrayDeque mNonBatchedOperationsでCreateViewOperation()をaddしただけで、UIOperationが抽象的なインタフェースであることに敏感に気づきます.
public interface UIOperation {
    void execute();
  }

やはり1つのインタフェースexecuteしかありません.それは自然にUIOperationを実現したクラスがたくさんあります.例えば、RemoveRootView Operation、ChangeJSResponderOperation、ShowPopupMenuOperationなどです.以前はUIポーリングがこれらのUIOperationを絶えず実行しているような気がしました.つまり、業務側は池に追加すればいいのです.このようなキューはAndroidの多くのシステムで遭遇しています.例えば、HandleやEventBusなど、興味のある読者は私の前のクラスの名前com/facebook/react/uimanager/UIViewOperationQueueを見てください.JAvaは大胆に中で輪訓のコードを探して、すぐにdispatchViewUpdatesの方法を発見しました
 void dispatchViewUpdates(final int batchId) {
                ...
                 if (nonBatchedOperations != null) {
                   for (UIOperation op : nonBatchedOperations) {
                     op.execute();
                   }
                 }

                 ...
           });
    }

スレッド配列にスレッドを追加し、それぞれのexecute()メソッドをforループ呼び出します.ここでは、CreateViewOperationの例を示します.
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);
    }
  }

executeメソッドの実行、すなわちmNativeViewHierarchyManagerの実行createView
public void createView(
      ThemedReactContext themedContext,
      int tag,
      String className,
      @Nullable ReactStylesDiffMap initialProps) {
        ...
    try {
      ViewManager viewManager = mViewManagers.get(className);

      View view = viewManager.createView(themedContext, mJSResponderHandler);
      mTagsToViews.put(tag, view);
      mTagsToViewManagers.put(tag, viewManager);

      view.setId(tag);
      if (initialProps != null) {
        viewManager.updateProperties(view, initialProps);
      }
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_VIEW);
    }
  }

ここのmViewManagersget(className)はclassNameに基づいて以前MainReactPackageに追加された様々なView Managementを見つけ、View ManagerのcreateViewメソッドを呼び出します.なぜなら、View Managerは親であるため、彼のcreateViewでは抽象メソッドcreateViewを呼び出します.次のコードを見てください.
 public final T createView(
      ThemedReactContext reactContext,
      JSResponderHandler jsResponderHandler) {
    T view = createViewInstance(reactContext);
    addEventEmitters(reactContext, view);
    if (view instanceof ReactInterceptingViewGroup) {
      ((ReactInterceptingViewGroup) view).setOnInterceptTouchEventListener(jsResponderHandler);
    }
    return view;
  }

 protected abstract T createViewInstance(ThemedReactContext reactContext);

createViewInstance抽象メソッドは、各サブクラスが実装しなければならないメソッドであり、Viewを構築しているメソッドであるか、例を挙げると、ReactTextInputManager
public class ReactTextInputManager extends BaseViewManager<ReactEditText, LayoutShadowNode> {

  /* package */ static final String REACT_CLASS = "AndroidTextInput";


  @Override
  public String getName() {
    return REACT_CLASS;
  }

  @Override
  public ReactEditText createViewInstance(ThemedReactContext context) {
    ReactEditText editText = new ReactEditText(context);
    int inputType = editText.getInputType();
    editText.setInputType(inputType & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE));
    editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
    editText.setTextSize(
        TypedValue.COMPLEX_UNIT_PX,
        (int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP)));
    return editText;
  }
}

彼のcreateView Instanceの方法はnew ReactEditText(context)で、ここで1つのViewが作成されたので、彼の属性はどこで設定しますか?安心してJSはすでに1つのViewを生成するデータをすべて持って帰ってきて、initialPropsの中でjsxの中のstyleで、viewManager.updateProperties(view, initialProps);次に、解析、プロパティの設定、rootViewでのサイズの測定、位置の決定、オリジナルUIレンダリングが完了します.期間の詳細は煩雑で、簡単に書くことはできません.ただ、プロセスを説明します.本当に詳細を描くことを知っている場合は、いくつかの重要なクラスをゆっくり解析する必要があります.必要な学生は自分で解読してください.
前文では今回、JSXがどのように最終的にオリジナルコントロールに変化するかを反転します.上の部分はオリジナルの描画が終わったと言えます.次はJSXコードで探し、JSXレイアウトがどのようにオリジナルに伝わるかを探します.