天天看点

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

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

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

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

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

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

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

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

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

答案在这个类UIManagerModule.java.

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代码技术 本文暂时不分析如何实现 只关注绘制部分代码  

public int addMeasuredRootView ( final SizeMonitoringFrameLayout rootView) {    final int tag = mNextRootViewTag ;    mNextRootViewTag += ROOT_VIEW_TAG_INCREMENT ;                 // 改变view tag   final ReactShadowNode rootCSSNode = new ReactShadowNode() ;    //js主要操作cssnode来控制native组件                                                                                                              //设置cssnode    rootCSSNode.setReactTag(tag) ;   final ThemedReactContext themedRootContext =        new ThemedReactContext(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(        new SizeMonitoringFrameLayout.OnSizeChangedListener() {          @Override          public void onSizeChanged ( final int width , final int height , int oldW , int oldH) {                        getReactApplicationContext().runOnNativeModulesQueueThread(                new Runnable() {                  @Override                  public void run () {                   updateRootNodeSize( rootCSSNode , width , height ) ;       //js层size改变的时候更新cssnode 进而更新native                  }               }) ;          }       }) ;   mShadowNodeRegistry .addRootNode(rootCSSNode) ;      if (UiThreadUtil. isOnUiThread ()) {      mNativeViewHierarchyManager .addRootView(tag , rootView , themedRootContext) ;    } else {      final Semaphore semaphore = new Semaphore( 0 ) ;                      //同步      getReactApplicationContext().runOnUiQueueThread(          new Runnable() {            @Override            public void run () {              mNativeViewHierarchyManager .addRootView( tag , rootView , themedRootContext ) ;              semaphore .release() ;            }         }) ;     try {       SoftAssertions. assertCondition (           semaphore.tryAcquire( 5000 , TimeUnit. MILLISECONDS ) ,            "Timed out adding root view" ) ;      } catch (InterruptedException e) {        throw new RuntimeException(e) ;      }   } return tag ; }

我们看下这个函数在哪里被调用了。启动ctrl f搜索大法。 当当当当。 ReactInstanceManager.java private void attachMeasuredRootViewToInstance ( 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中    @Nullable Bundle 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 void createView ( int tag , String className , int rootViewTag , 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 void enqueueCreateView ( int rootViewTagForContext , int viewReactTag , String viewClassName ,      @Nullable CatalystStylesDiffMap initialProps) {    mOperations .add(        new CreateViewOperation( rootViewTagForContext ,      //内部类            viewReactTag , viewClassName , initialProps)) ; }

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

NativeViewHierarchyManager.java public void createView (      int rootViewTagForContext , int tag , String className ,      @Nullable CatalystStylesDiffMap 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。