React Native之底层源码分析篇

学习React-Native有一段时间了。今天就从源码的角度来分析下React-Native底层的通信机制。了解下底层是如何通信的对开发也有所好处。

概要

先大概讲一下React-Native的通信过程。RN主要的通信在于java与js之间,平常我们写的jsx代码最终会调用到原生的View。上一篇博客我们也了解到了要新建一个原生模块需要在java层和js层分别写一个Module,那这彼此之间联系是如何实现的呢?

层次结构

RN总共分为三层,java层,C++层,js层。借用一幅图来看下:
这里写图片描述
Java层:java层就是app原生代码,它通过启动C++层的javascript解析器javascriptCore来执行js代码,从而构建原生UI等。java层依赖于众多优秀开源库,在图片处理使用的是Fresco,网络通信使用的是okhttp,Json解析工具用jackson,动画库用NineOldAndroids等,在java层原生的功能均封装为Module,如Toast和Log等。
C++层:c++层最主要是封装了JavaScriptCore,它是一个全新的支持ES6的webKit。Bridge连接了java与js之间的通信。解析js文件是通过JSCExectutor进行的。
Js层:主要处理事件分发及UI Layout,平常开发最常用的。通用jsx来写业务代码,通过flexbox来实现布局。不依赖DOM。由于react有 DOM diff这个算法,所以它的效率非常高。

通信机制

在Java层与Js层的bridge分别存有相同一份模块配置表,Java与Js互相通信时,通过将里配置表中的moduleID,methodID转换成json数据的形式传递给到C++层,C++层传送到js层,通过js层的的模块配置表找到对应的方法进行执行,如果有callback,则回传给java层。这里只是大概介绍,后面会有详细讲解。

主要流程与主要类

先看下java层的流程图:
这里写图片描述

  • ReactInstanceManager:主要是用来创建及管理CatalyInstance的实例的上层接口,控制开发调试,生命周期与ReactRootView所在activity保持一致。
  • ReactRootView:为启动入口核心类,负责监听及分发事件并重新渲染元素,App启动后,其将作为App的root view。
  • CatalystInstance:提供Java与Js互通的环境,创建Java模块注册表及Javascript模块注册表,并遍历实例化模块,最后通过ReactBridge将Js Bundle传送到Js引擎。
  • JSBuilderLoader:缓存了JsBundle的信息,封装了上层加载JsBundle相关接口,CatalystInstance通过其间接调用ReactBridge去加载文件。
  • NativeModuleRegistry:Java层模块注册表,即暴露给Js的API集合。
  • JavascriptModuleRegistry:Js层模块注册表,负责将所有JavaScriptModule注册到CatalystInstance。
  • CoreModulePackage:CoreModulesPackage里面定义了RN框架核心的一些Java和JS的module,创建NativeModules&JsModules组件模块。

源码分析

加载Module

首先看MainActivity的

  protected List<ReactPackage> getPackages() {
        return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
        new  AppReactPackage()
        );
    }

AppReactPackage是我们自定义的一个ReactPackage,也就是说如果自己定义了新组件,要在这里添加。看下ReactActivity,看它的onCreate方法:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(this.getUseDeveloperSupport() && VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(this)) {
            Intent mReactRootView = new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION");
            this.startActivity(mReactRootView);
            FLog.w("React", "Overlay permissions needs to be granted in order for react native apps to run in dev mode");
            Toast.makeText(this, "Overlay permissions needs to be granted in order for react native apps to run in dev mode", 1).show();
        }

        this.mReactInstanceManager = this.createReactInstanceManager();
        ReactRootView mReactRootView1 = this.createRootView();
        mReactRootView1.startReactApplication(this.mReactInstanceManager, this.getMainComponentName(), this.getLaunchOptions());
        this.setContentView(mReactRootView1);
    }

主要完成三个功能,通过createReactInstanceManager创建ReactInstanceManager,它主要是用来创建及管理CatalyInstance的实例的上层接口。第二步是通过createRootView来创建ReactRootView。最后调用ReactRootView的startReactApplication来启动应用,并把它当作rootview setContentView到界面上。重点看startReactApplication方法:

   public void startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle launchOptions) {
        UiThreadUtil.assertOnUiThread();
        Assertions.assertCondition(this.mReactInstanceManager == null, "This root view has already been attached to a catalyst instance manager");
        this.mReactInstanceManager = reactInstanceManager;
        this.mJSModuleName = moduleName;
        this.mLaunchOptions = launchOptions;
        if(!this.mReactInstanceManager.hasStartedCreatingInitialContext()) {
            this.mReactInstanceManager.createReactContextInBackground();
        }

        if(this.mWasMeasured && this.mIsAttachedToWindow) {
            this.mReactInstanceManager.attachMeasuredRootView(this);
            this.mIsAttachedToInstance = true;
            this.getViewTreeObserver().addOnGlobalLayoutListener(this.getKeyboardListener());
        } else {
            this.mAttachScheduled = true;
        }

    }

mJSModuleName是与前端约定好所要启动的JS Application Name。先看createReactContextInBackground方法,它位于ReactInstanceManager的实现类ReactInstanceManagerImpl中:

public void recreateReactContextInBackground() {
    Assertions.assertCondition(this .mHasStartedCreatingInitialContext , "recreateReactContextInBackground should only be called after the initial createReactContextInBackground call.") ;
    this. recreateReactContextInBackgroundInner() ;
}

createReactContextInBackground最终调用到recreateReactContextInBackgroundInner:

private void recreateReactContextInBackgroundInner() {
    UiThreadUtil.assertOnUiThread();
    if( this.mUseDeveloperSupport && this.mJSMainModuleName != null) {
        if (this.mDevSupportManager.hasUpToDateJSBundleInCache()) {
            this .onJSBundleLoadedFromServer() ;
        } else if(this .mJSBundleFile == null) {
            this .mDevSupportManager.handleReloadJS() ;
        } else {
            this .mDevSupportManager.isPackagerRunning( new PackagerStatusCallback() {
                public void onPackagerStatusFetched( final boolean packagerIsRunning) {
                    UiThreadUtil.runOnUiThread( new Runnable() {
                        public void run() {
                            if(packagerIsRunning) {
                                ReactInstanceManagerImpl.this .mDevSupportManager.handleReloadJS() ;
                            } else {
                                ReactInstanceManagerImpl.this .recreateReactContextInBackgroundFromBundleFile() ;
                            }

                        }
                    }) ;
                }
            });
        }

    } else {
        this .recreateReactContextInBackgroundFromBundleFile() ;
    }
}

接着调用recreateReactContextInBackgroundFromBundleFile:

   private void recreateReactContextInBackgroundFromBundleFile() {
        this.recreateReactContextInBackground(new com.facebook.react.bridge.JSCJavaScriptExecutor.Factory(), JSBundleLoader.createFileLoader(this.mApplicationContext, this.mJSBundleFile));
    }

经过一系列的周转,最后调用到了recreateReactContextInBackground:

private void recreateReactContextInBackground(com.facebook.react.bridge.JavaScriptExecutor.Factory jsExecutorFactory, JSBundleLoader jsBundleLoader) {
    UiThreadUtil.assertOnUiThread();
    ReactInstanceManagerImpl.ReactContextInitParams initParams = new ReactInstanceManagerImpl.ReactContextInitParams(jsExecutorFactory , jsBundleLoader);
    if( this.mReactContextInitAsyncTask == null) {
        this .mReactContextInitAsyncTask = new ReactInstanceManagerImpl.ReactContextInitAsyncTask( null);
        this.mReactContextInitAsyncTask.execute( new ReactInstanceManagerImpl.ReactContextInitParams[]{initParams}) ;
    } else {
        this .mPendingReactContextInitParams = initParams ;
    }

}

上面代码通过ReactContextInitAsyncTask这个AsyncTask来初始化ReactCotext。

 private final class ReactContextInitAsyncTask extends AsyncTask<ReactInstanceManagerImpl.ReactContextInitParams, Void, ReactInstanceManagerImpl.Result<ReactApplicationContext>> {
        private ReactContextInitAsyncTask() {
        }

        protected void onPreExecute() {
            if(ReactInstanceManagerImpl.this.mCurrentReactContext != null) {
                ReactInstanceManagerImpl.this.tearDownReactContext(ReactInstanceManagerImpl.this.mCurrentReactContext);
                ReactInstanceManagerImpl.this.mCurrentReactContext = null;
            }

        }

        protected ReactInstanceManagerImpl.Result<ReactApplicationContext> doInBackground(ReactInstanceManagerImpl.ReactContextInitParams... params) {
            Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);

            try {
                JavaScriptExecutor e = params[0].getJsExecutorFactory().create(ReactInstanceManagerImpl.this.mJSCConfig == null?new WritableNativeMap():ReactInstanceManagerImpl.this.mJSCConfig.getConfigMap());
                return ReactInstanceManagerImpl.Result.of((Object)ReactInstanceManagerImpl.this.createReactContext(e, params[0].getJsBundleLoader()));
            } catch (Exception var3) {
                return ReactInstanceManagerImpl.Result.of(var3);
            }
        }

        protected void onPostExecute(ReactInstanceManagerImpl.Result<ReactApplicationContext> result) {
            try {
                ReactInstanceManagerImpl.this.setupReactContext((ReactApplicationContext)result.get());
            } catch (Exception var6) {
                ReactInstanceManagerImpl.this.mDevSupportManager.handleException(var6);
            } finally {
                ReactInstanceManagerImpl.this.mReactContextInitAsyncTask = null;
            }

            if(ReactInstanceManagerImpl.this.mPendingReactContextInitParams != null) {
                ReactInstanceManagerImpl.this.recreateReactContextInBackground(ReactInstanceManagerImpl.this.mPendingReactContextInitParams.getJsExecutorFactory(), ReactInstanceManagerImpl.this.mPendingReactContextInitParams.getJsBundleLoader());
                ReactInstanceManagerImpl.this.mPendingReactContextInitParams = null;
            }

        }

ReactContextInitAsyncTask为创建ReactContext的核心类,随后,调用createReactContext进一步创建ReactContext。在创建完React Context后会调用setUpReactContext,将ReactRootView做为Root View传递给UIManagerModule,调用AppRegistry的runApplication去启动Js Application等。看createReactContext的代码:

private ReactApplicationContext createReactContext(JavaScriptExecutor jsExecutor , JSBundleLoader jsBundleLoader) {
    FLog.i("React" , "Creating react context.");
    ReactMarker.logMarker( "CREATE_REACT_CONTEXT_START" );
    this.mSourceUrl = jsBundleLoader.getSourceUrl() ;
    Builder nativeRegistryBuilder = new Builder();
    com.facebook.react.bridge.JavaScriptModulesConfig.Builder jsModulesBuilder = new 
  • 10
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
React Native中的VirtualizedList组件是用于高效渲染大量数据的列表视图组件。虚拟化列表在滚动时只加载可见部分的数据,而非一次性渲染所有的列表项,从而提高了性能。 VirtualizedList组件的源码解析可以从以下几个方面进行: 1. 数据源:VirtualizedList接受一个名为data的props,用于表示要渲染的数据源。该数据源可以是一个数组或者是一个带有迭代方法的对象。在源码中,会使用this.props.data来获取传入的数据源。 2. 视图创建:在VirtualizedList组件的源码中,会通过ViewabilityHelper类型来管理可见项和滚动状态。ViewabilityHelper会根据滚动位置计算哪些列表项是可见的,并且会在必要时创建和删除列表项的视图。 3. 渲染性能优化:为了提高滚动的性能,VirtualizedList使用了windowSize属性,该属性定义了可见区域外额外渲染的列表项数量。源码中会根据滚动的位置,动态加载和卸载视图,以保证只渲染用户可见的列表项。 4. 列表项更新:当列表项的数据发生变化时,VirtualizedList会根据数据的变化更新相应的列表项。在源码中,会使用shouldItemUpdate()方法来判断列表项是否需要更新。 5. 交互处理:VirtualizedList会对滚动事件、滚动结束事件等进行处理,以便实现列表的滚动效果。在源码中,会使用onScroll()和onScrollEndDrag()等方法来处理相关交互事件。 总的来说,VirtualizedList作为React Native中高效渲染大量数据的列表视图组件,其源码实现了数据源管理、视图创建和销毁、渲染性能优化、列表项更新、交互处理等功能。通过深入源码分析,可以更好地了解VirtualizedList组件的工作原理,并能更好地使用和定制该组件。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值