这是之前在android版刚出来不久时在内部发的一篇浅析,现在同步上来,这之间,版本由于快速变动或多或少有些许出入,但大体思路暂时未变。
react native的android
版于上周发布,然后花了点时间试了下,从几个demo的体验来看都挺流畅,具体环境搭建与基本介绍,可以看看官方的文档说明。
之后周末又花了点时间看了下内部的实现,这里主要来简单介绍下java层是如何与js绑定的。
整体架构上,可以分为java层<->c++层<->js
层,其中java层作为入口,主要是对整体流程的控制,模块的配置。c++层是对JavaScriptCore接口的封装(每个应用自己背一个js解释器),直接执行js脚本并获取返回值。js层则是界面的布局与事件的响应处理。
启动
MoviesActivity.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class MoviesActivity extends Activity implements DefaultHardwareBackBtnHandler { private ReactInstanceManager mReactInstanceManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mReactInstanceManager = ReactInstanceManager.builder() .setApplication(getApplication()) .setBundleAssetName("MoviesApp.android.bundle") .setJSMainModuleName("Examples/Movies/MoviesApp.android") .addPackage(new MainReactPackage()) .setUseDeveloperSupport(true) .setInitialLifecycleState(LifecycleState.RESUMED) .build(); ((ReactRootView) findViewById(R.id.react_root_view)) .startReactApplication(mReactInstanceManager, "MoviesApp", null); }
|
入口上,主要是在相应使用React的Activity中加入一个ReactRootView,在Activity的onCreate中,通过ReactRootView启动React。这里的addPackage
是配置应用所需的java模块与js模块,这个后面会细说。然后React的启动中主要会构造React的context,加载模块配置表,初始化CatalystInstance
(java与js调用的高层接口) 与ReactBridge
(jni接口封装),最后通过AppRegistry
调用到js层的入口完成启动。
ReactInstanceManager.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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); @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); }
|
模块注册
React Native中,需要用户自己定义需要的配置ReactPackage
,显式的声明哪些java类或js脚本是api,是两边都需要的。的然后在启动中注册后,会同时在java端与js端共用一份模块配置表,主要以{moduleID, methodID}
定位到某个java模块或js模块的方法。
CoreModulesPackage.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| class CoreModulesPackage implements ReactPackage { private final ReactInstanceManager mReactInstanceManager; private final DefaultHardwareBackBtnHandler mHardwareBackBtnHandler; CoreModulesPackage( ReactInstanceManager reactInstanceManager, DefaultHardwareBackBtnHandler hardwareBackBtnHandler) { mReactInstanceManager = reactInstanceManager; mHardwareBackBtnHandler = hardwareBackBtnHandler; } @Override public List<NativeModule> createNativeModules( ReactApplicationContext catalystApplicationContext) { return Arrays.<NativeModule>asList( new AnimationsDebugModule( catalystApplicationContext, mReactInstanceManager.getDevSupportManager().getDevSettings()), new AndroidInfoModule(), new DeviceEventManagerModule(catalystApplicationContext, mHardwareBackBtnHandler), new ExceptionsManagerModule(mReactInstanceManager.getDevSupportManager()), new Timing(catalystApplicationContext), ... } @Override public List<Class<? extends JavaScriptModule>> createJSModules() { return Arrays.asList( DeviceEventManagerModule.RCTDeviceEventEmitter.class, JSTimersExecution.class, RCTEventEmitter.class, AppRegistry.class, ReactNative.class, DebugComponentOwnershipModule.RCTDebugComponentOwnership.class); } ...
|
模块配置
NativeModuleRegistry.java1 2 3 4 5 6 7
| public class NativeModuleRegistry { private final ArrayList<ModuleDefinition> mModuleTable; private final Map<Class<NativeModule>, NativeModule> mModuleInstances; private final String mModuleDescriptions; private final ArrayList<OnBatchCompleteListener> mBatchCompleteListenerModules; ...
|
java模块注册表
JavaScriptModuleRegistry.java1 2 3 4 5
| class JavaScriptModuleRegistry { private final HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> mModuleInstances; ... }
|
js模块注册表
ToastModule.java1 2 3 4 5 6 7 8 9 10 11 12 13
| public class ToastModule extends ReactContextBaseJavaModule { public ToastModule(ReactApplicationContext reactContext) { super(reactContext); } ... @ReactMethod public void show(String message, int duration) { Toast.makeText(getReactApplicationContext(), message, duration).show(); } }
|
java模块主要是通过@ReactMethod
注解表明该方法为可以导出到js的方法
AppRegistry.java1 2 3 4 5 6
| * JS module interface - main entry point for launching react application for a given key. */ public interface AppRegistry extends JavaScriptModule { void runApplication(String appKey, WritableMap appParameters); }
|
JavaScriptModuleRegistry.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| private static class JavaScriptModuleInvocationHandler implements InvocationHandler { private final CatalystInstance mCatalystInstance; private final JavaScriptModuleRegistration mModuleRegistration; public JavaScriptModuleInvocationHandler( CatalystInstance catalystInstance, JavaScriptModuleRegistration moduleRegistration) { mCatalystInstance = catalystInstance; mModuleRegistration = moduleRegistration; } @Override public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String tracingName = mModuleRegistration.getTracingName(method); mCatalystInstance.callFunction( mModuleRegistration.getModuleId(), mModuleRegistration.getMethodId(method), Arguments.fromJavaArgs(args), tracingName); return null; } }
|
js模块是继承JavaScriptModule的interface,与之对应的是在js侧有个同名的js脚本,运行时通过动态代理
技术,生成实现,在模块配置表中查找到该js,然后发起调用。
CatalystInstance.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| private void initializeBridge( JavaScriptExecutor jsExecutor, NativeModuleRegistry registry, JavaScriptModulesConfig jsModulesConfig, JSBundleLoader jsBundleLoader) { mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread(); Assertions.assertCondition(mBridge == null, "initializeBridge should be called once"); mBridge = new ReactBridge( jsExecutor, new NativeModulesReactCallback(), mCatalystQueueConfiguration.getNativeModulesQueueThread()); mBridge.setGlobalVariable( "__fbBatchedBridgeConfig", buildModulesConfigJSONProperty(registry, jsModulesConfig)); jsBundleLoader.loadScript(mBridge); }
|
将模块配置表作为全局变量导入到js层,
调用
java与js之前的调用,两边都是以{moduleID, methodID}
的形式作为一个call
,然后一端在之前的模块配置表里查找注册的模块与方法。
java调用js
是直接通过c++层对JavaScriptCore封装的接口调用到相应的脚本。
ReactBridge.java1 2 3 4 5 6
| public native void loadScriptFromAssets(AssetManager assetManager, String assetName); public native void loadScriptFromNetworkCached(String sourceURL, @Nullable String tempFileName); public native void callFunction(int moduleId, int methodId, NativeArray arguments); public native void invokeCallback(int callbackID, NativeArray arguments); public native void setGlobalVariable(String propertyName, String jsonEncodedArgument); ...
|
OnLoad.cpp1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static void callFunction(JNIEnv* env, jobject obj, jint moduleId, jint methodId, NativeArray::jhybridobject args) { auto bridge = extractRefPtr<Bridge>(env, obj); auto arguments = cthis(wrap_alias(args)); std::vector<folly::dynamic> call{ (double) moduleId, (double) methodId, std::move(arguments->array), }; try { bridge->executeJSCall("BatchedBridge", "callFunctionReturnFlushedQueue", std::move(call)); } catch (...) { translatePendingCppExceptionToJavaException(); } }
|
c++层,直接调用BatchedBridge.js
即MessageQueue.js
中的callFunctionReturnFlushedQueue
,最终会加载js对应的模块并调用到相应方法。
MessageQueue.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| callFunctionReturnFlushedQueue(module, method, args) { guard(() => this.__callFunction(module, method, args)); return this.flushedQueue(); } ... __callFunction(module, method, args) { BridgeProfiling.profile(() => `${module}.${method}(${stringifySafe(args)})`); if (isFinite(module)) { method = this._methodTable[module][method]; module = this._moduleTable[module]; } if (__DEV__ && SPY_MODE) { console.log('N->JS : ' + module + '.' + method + '(' + JSON.stringify(args) + ')'); } module = this._require(module); module[method].apply(module, args); BridgeProfiling.profileEnd(); }
|
js调用java
React Native的设计是js不会
直接调用java,而是将调用的call插入到一个js层的一个MessageQueue
中,等
java过来调用。
MessageQueue.js1 2 3 4 5 6 7 8 9 10 11 12
| class MessageQueue { constructor(remoteModules, localModules, customRequire) { this.RemoteModules = {}; this._require = customRequire || require; this._queue = [[],[],[]]; this._moduleTable = {}; this._methodTable = {}; this._callbacks = []; this._callbackID = 0; ...
|
MessageQueue.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| __nativeCall(module, method, params, onFail, onSucc) { if (onFail || onSucc) { (this._callbackID > (1 << 5)) && (this._debugInfo[this._callbackID >> 5] = null); this._debugInfo[this._callbackID >> 1] = [module, method]; onFail && params.push(this._callbackID); this._callbacks[this._callbackID++] = onFail; onSucc && params.push(this._callbackID); this._callbacks[this._callbackID++] = onSucc; } this._queue[MODULE_IDS].push(module); this._queue[METHOD_IDS].push(method); this._queue[PARAMS].push(params); if (__DEV__ && SPY_MODE && isFinite(module)) { console.log('JS->N : ' + this._remoteModuleTable[module] + '.' + this._remoteMethodTable[module][method] + '(' + JSON.stringify(params) + ')'); } }
|
具体是在java层主动调用或各种事件到来时(触摸,刷新,timer…),都会调用上文说的java层的callFunction,从而最终调用上文说到的callFunctionReturnFlushedQueue
,注意该方法会在执行相应js调用后调用flushedQueue
并返回MessageQueue.
MessageQueue.js1 2 3 4 5 6 7 8
| flushedQueue() { BridgeProfiling.profile('JSTimersExecution.callImmediates()'); guard(() => JSTimersExecution.callImmediates()); BridgeProfiling.profileEnd(); let queue = this._queue; this._queue = [[],[],[]]; return queue[0].length ? queue : null; }
|
返回的queue格式化为json经由c++返回给java
Bridge.cpp1 2 3 4 5 6 7
| void executeJSCall( const std::string& moduleName, const std::string& methodName, const std::vector<folly::dynamic>& arguments) { auto returnedJSON = m_jsExecutor->executeJSCall(moduleName, methodName, arguments); m_callback(parseMethodCalls(returnedJSON)); }
|
OnLoad.cpp1 2 3 4 5 6 7 8 9 10 11
| static void create(JNIEnv* env, jobject obj, jobject executor, jobject callback, jobject callbackQueueThread) { auto weakCallback = createNew<WeakReference>(callback); auto weakCallbackQueueThread = createNew<WeakReference>(callbackQueueThread); auto bridgeCallback = [weakCallback, weakCallbackQueueThread] (std::vector<MethodCall> calls) { dispatchCallbacksToJava(weakCallback, weakCallbackQueueThread, std::move(calls)); }; auto nativeExecutorFactory = extractRefPtr<JSExecutorFactory>(env, executor); auto bridge = createNew<Bridge>(nativeExecutorFactory, bridgeCallback); setCountableForJava(env, obj, std::move(bridge)); }
|
CatalysInstance.java1 2 3 4 5 6 7 8 9 10 11 12 13
| private class NativeModulesReactCallback implements ReactCallback { @Override public void call(int moduleId, int methodId, ReadableNativeArray parameters) { mCatalystQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread(); if (mDestroyed) { return; } mJavaRegistry.call(CatalystInstance.this, moduleId, methodId, parameters); }
|
最终java层依旧是查表调用对应的方法。
线程
线程方面 主要分为了三种线程
UIQueueThread
android本身的ui线程,最终绘制肯定还得在这里
NativeModulesQueueThread
js调过来的native module的方法执行的线程
JSQueueThread
js解释器线程 CatalystQueueConfiguration.java1 2 3 4 5 6 7 8 9 10 11 12 13 14
| * Specifies which {@link MessageQueueThread}s must be used to run the various contexts of * execution within catalyst (Main UI thread, native modules, and JS). Some of these queues *may* be * the same but should be coded against as if they are different. * * UI Queue Thread: The standard Android main UI thread and Looper. Not configurable. * Native Modules Queue Thread: The thread and Looper that native modules are invoked on. * JS Queue Thread: The thread and Looper that JS is executed on. */ public class CatalystQueueConfiguration { private final MessageQueueThread mUIQueueThread; private final MessageQueueThread mNativeModulesQueueThread; private final MessageQueueThread mJSQueueThread;
|
总结
以上就是React Native Android版java和js间相互调用的主要实现,从demo的体验感觉整体上比较流畅,起码简单的页面感知上和native区分不太开。而且只要reload一遍js bundle,不用重启不用切换,页面立即原地更新,这个应该是一个很大的卖点。
代码上整体分层挺清晰,集合了包括fresco/okhttp/botls/boost/folly等不少开源库,java层用了动态代理,c++层的代码是用c++11封装的,js中也是使用了不少es6的新特性,还有jsx,css layout什么的,模式上也是状态驱动UI,整体实现感觉很是前卫,但是要完全掌握可能要java/c++/js三修,对现有的开发模式是一个新的挑战。而且由于依赖库太多,导致安装包略肿大,光so就有4.5MB,这给集成到现有项目带来了不少问题。
另外,由于监听了Choreographer
的事件,使用了些高版本的api,导致4.1
以下不能使用React。
而且实际使用中,由于react本身其实主要还是帮助做绑定,导致稍微复杂一点的界面和逻辑,还是少不了native的大量参与,除非导出的组件已经足够用或者界面足够简单。
总的来说,虽然可能刚出来还存在不少问题,包大小与开发方式也需要进一步的探索,但react native的整体设计与实现还是很值得学习的,或许今后会带来app开发新的思路吧。