react-native源码分析系列五 绘制js组件

来源:互联网 发布:软件开发需求调研 编辑:程序博客网 时间:2024/05/21 09:27

上篇分析了负责分发touch的ReatRootView,这篇文章继续分析view的绘制。

react-native绘制view的思路是将js写的控件映射到native的控件,通过addView之类的函数将js的控件添加到reactRootView.

首先看一个我个人项目中的例子。ui长这样。


用ddms的hierarchy view抓一下看一下。



可以看到有ReactDrawerLayout ReactImageView ReactTextView等,这些都是java的控件。因此我们看到js写的界面

全都被解析成了native的对应控件。具体是怎么实现的呢?

答案在这个类UIManagerModule.java.

/**

 * <p>Native module toallow JS to create and update native Views.</p>

 *<h2>== Transactional Requirement ==</h2>

 * A requirement of this class is to make sure that transactional UI updates occur all at, meaning

 * thatno intermediate state is ever rendered to the screen. For example, if a JS application

 * update changes the background of View A to blue and the width of View B to 100, both need to

 * appear at once. Practically, this means that all UI update code related to a single transaction

 * must be executed as a single code block on the UI thread. Executing as multiple code blocks

 * could allow the platform UI system to interrupt and render a partial UI state. 

 *    //这里说 js的一批变化必须在java层要一起处理 不能出现中间态

 * <p>To facilitate this, this module enqueues operations that are then applied to native view

 * hierarchy through {@linkNativeViewHierarchyManager} at the end of each transaction.

 *<h2>== CSSNodes ==</h2>

 * In order to allow layout and measurement to occur on a non-UI thread, this module also

 * operates on intermediate CSSNode objects that correspond to a native view. These CSSNode are able

 * to calculate layout according to their styling rules, and then the resulting x/y/width/height of

 * that layout is scheduled as an operation that will be applied to native view hierarchy at the end

 * of current batch.     //js通过操作cssnode来操作view 计算好全部的cssnode后再统一刷新ui(mainthread不能做耗时的工作

 */

public UIManagerModule(ReactApplicationContext reactContext, List<ViewManager> viewManagerList) {
  super(reactContext);
 mViewManagers= new ViewManagerRegistry(viewManagerList);               //各种控件的manager
 mEventDispatcher = new EventDispatcher(reactContext);                       //这个上篇文章见过 dispatch touch等事件的
 mNativeViewHierarchyManager = new NativeViewHierarchyManager(
     mAnimationRegistry,mViewManagers);                                                   //统一管理视图变化
 mOperationsQueue = new UIViewOperationQueue(reactContext,this,mNativeViewHierarchyManager,
     mAnimationRegistry);                                                                             //js发过来的operation统一处理
  mNativeViewHierarchyOptimizer = new NativeViewHierarchyOptimizer(mOperationsQueue,
     mShadowNodeRegistry);
  DisplayMetrics displayMetrics = reactContext.getResources().getDisplayMetrics();
  DisplayMetricsHolder.setDisplayMetrics(displayMetrics);
  mModuleConstants = UIManagerModuleConstantsHelper.createConstants(displayMetrics,viewManagerList);
                         //rn封装的所有控件 都在这里 包括控件的属性比如width height textView具有text
                         //控件可以执行的操作 比如drawLayout可以打开关闭等
  reactContext.addLifecycleEventListener(this);
}              //当然这个类还涉及到js如何调用native代码技术 本文暂时不分析如何实现 只关注绘制部分代码  

/**
 * Registers a new root view.JS can use the returned tag with manageChildren to add/remove
 * children to this view.
 */
public int addMeasuredRootView(finalSizeMonitoringFrameLayout rootView) {
  final inttag = mNextRootViewTag;
  mNextRootViewTag+= ROOT_VIEW_TAG_INCREMENT;                 // 改变view tag
  final ReactShadowNode rootCSSNode = newReactShadowNode();   //js主要操作cssnode来控制native组件
                                                                                                             //设置cssnode
  rootCSSNode.setReactTag(tag);
  final ThemedReactContext themedRootContext =
      newThemedReactContext(getReactApplicationContext(),rootView.getContext());
  rootCSSNode.setThemedContext(themedRootContext);                     
  if(rootView.getLayoutParams() != null&&
      rootView.getLayoutParams().width> 0 &&
      rootView.getLayoutParams().height> 0) {
    rootCSSNode.setStyleWidth(rootView.getLayoutParams().width);
    rootCSSNode.setStyleHeight(rootView.getLayoutParams().height);
  }else {
    rootCSSNode.setStyleWidth(rootView.getWidth());
    rootCSSNode.setStyleHeight(rootView.getHeight());
  }
  rootCSSNode.setViewClassName("Root");                                        //设置cssnode
  rootView.setOnSizeChangedListener(
      newSizeMonitoringFrameLayout.OnSizeChangedListener() {
        @Override
        public voidonSizeChanged(final intwidth, final intheight, intoldW, intoldH) {          
            getReactApplicationContext().runOnNativeModulesQueueThread(
              newRunnable() {
                @Override
                public voidrun() {
                  updateRootNodeSize(rootCSSNode,width, height);       //js层size改变的时候更新cssnode 进而更新native
                }
              });
        }
      });
  mShadowNodeRegistry.addRootNode(rootCSSNode);  
  if (UiThreadUtil.isOnUiThread()) {
    mNativeViewHierarchyManager.addRootView(tag,rootView,themedRootContext);
  }else {
    finalSemaphore semaphore = newSemaphore(0);                      //同步
    getReactApplicationContext().runOnUiQueueThread(
        newRunnable() {
          @Override
          public voidrun() {
            mNativeViewHierarchyManager.addRootView(tag,rootView,themedRootContext);
            semaphore.release();
          }
        });
    try {
      SoftAssertions.assertCondition(
          semaphore.tryAcquire(5000,TimeUnit.MILLISECONDS),
          "Timed out adding root view");
    }catch (InterruptedException e) {
      throw newRuntimeException(e);
    }
  }
returntag;
}

我们看下这个函数在哪里被调用了。启动ctrl f搜索大法。
当当当当。
ReactInstanceManager.java
private voidattachMeasuredRootViewToInstance(ReactRootView rootView,
    CatalystInstance catalystInstance) {
  UiThreadUtil.assertOnUiThread();
  rootView.removeAllViews();
  rootView.setId(View.NO_ID);
  UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
  int rootTag = uiManagerModule.addMeasuredRootView(rootView);  //xml文件必须要有一个reactRootView 
                                                                                                         //将这个view注册到js中
  @NullableBundle launchOptions = rootView.getLaunchOptions();
  WritableMap initialProps = launchOptions !=null? Arguments.fromBundle(launchOptions)
      : Arguments.createMap();
  String jsAppModuleName = rootView.getJSModuleName();
  WritableNativeMap appParams =new WritableNativeMap();
  appParams.putDouble("rootTag",rootTag);
  appParams.putMap("initialProps",initialProps);
 catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName,appParams);
                                                                                                       //第一篇文章我们就见过它 这个整个rn app启动的函数
      //可以想象到的是 AppRegistry.js一定将这个传入的view 作为root 然后解析其他组件 解析好后通过一些函数动态
     //加到这个rootview中
}

动态生成一个js对应的java view组件函数是这个。
@ReactMethod                                                                                   //这个annotation说明是java暴露给js的函数
public voidcreateView(inttag, String className, introotViewTag,ReadableMap props) {
  ViewManager viewManager =mViewManagers.get(className);                           //拿到js层对应的java层控件的manager
  ReactShadowNode cssNode = viewManager.createShadowNodeInstance();       //设置cssnode
  ReactShadowNode rootNode =mShadowNodeRegistry.getNode(rootViewTag);
  cssNode.setReactTag(tag);
  cssNode.setViewClassName(className);
  cssNode.setRootNode(rootNode);
  cssNode.setThemedContext(rootNode.getThemedContext());                          //设置cssnode
  mShadowNodeRegistry.addNode(cssNode);
  CatalystStylesDiffMap styles =null;
  if (props != null) {
    styles =new CatalystStylesDiffMap(props);
    cssNode.updateProperties(styles);
  }
  if(!cssNode.isVirtual()) {
    mNativeViewHierarchyOptimizer.handleCreateView(cssNode,rootViewTag,styles);  //通过cssnode创建出控件
  }
}

NativeViewHierarchyOptimizer.java
public void handleCreateView()
mUIViewOperationQueue.enqueueCreateView(rootViewTag,tag, node.getViewClass(),initialProps);

UIViewOperationQueue.java
public voidenqueueCreateView(introotViewTagForContext,intviewReactTag,String viewClassName,
    @NullableCatalystStylesDiffMap initialProps) {
  mOperations.add(
      newCreateViewOperation(rootViewTagForContext,      //内部类
          viewReactTag,viewClassName,initialProps));
}

public CreateViewOperation(
    introotViewTagForContext,inttag,String className,
    @NullableCatalystStylesDiffMap initialProps) {
  super(tag);
  mRootViewTagForContext= rootViewTagForContext;
  mClassName= className;
  mInitialProps= initialProps;
}
@Override
public void execute() {
  mNativeViewHierarchyManager.createView(
      mRootViewTagForContext,mTag,mClassName,mInitialProps);
}


NativeViewHierarchyManager.java
public voidcreateView(
    introotViewTagForContext,inttag,String className,
    @NullableCatalystStylesDiffMap initialProps) {
  UiThreadUtil.assertOnUiThread();
  ViewManager viewManager =mViewManagers.get(className);
  View view =viewManager.createView(mRootViewsContext.get(rootViewTagForContext),mJSResponderHandler);
          //viewManager是一个虚基类 因此由具体的控件manager负责生成一个view
  mTagsToViews.put(tag,view);
  mTagsToViewManagers.put(tag,viewManager);   //生成一个view后 存入mTagsToViews和mTagToViewManager需要时再取
  view.setId(tag);        //可以reuse
  if (initialProps != null) {
    viewManager.updateProperties(view,initialProps);
  }
}      //因此UIManagerModule生成一个js对应的view是由NativeHierarchyManager调用相应的view的manager来实现的


相应的生成view后增加删除view的操作是由UIManagerModule暴露给js的函数manageChildren实现的。
UIManagerModule的managerChildren函数和上面的函数一样也是最终由NativeHierarchyManager调用view的manager来实现的。

ok~这篇文章就到这里啦~
这篇文章是rn view系列的第二篇。由于view的内容非常多,本文重点关注了view的绘制部分。
通过上面的分析,
1 我们看到 rn管理view的类是UIManagerModule,
2 rn的ui必须有一个ReactRootView用来当作js view的根,由程序调用js的AppRegistry.runApplication这个入口前注册到js。
3 js解析js部分的控件hierarchy 并通过js的UIManagerModule暴露给js的createview addView removeView等接口操作native层的组件。
4 UIManagerModule完成生成 添加 删除js对应view的view的方法是,rn自己封装了一套React-native的组件,view及对应的manager。其中view统一命名成React开头,比如textView是ReactTextView,manager的命名规则也是一样。manager其实有点类似于builder设计模式,用来负责生成对应的view,比如ReactTextView对应的就是ReactTextViewManager。


0 0
原创粉丝点击