这是之前在android版刚出来不久时在内部发的一篇浅析,现在同步上来,这之间,版本由于快速变动或多或少有些许出入,但大体思路暂时未变。
react native的android
版于上周发布,然后花了点时间试了下,从几个demo的体验来看都挺流畅,具体环境搭建与基本介绍,可以看看官方的文档说明。 之后周末又花了点时间看了下内部的实现,这里主要来简单介绍下java层是如何与js绑定的。
整体架构上,可以分为java层<->c++层<->js
层,其中java层作为入口,主要是对整体流程的控制,模块的配置。c++层是对JavaScriptCore接口的封装(每个应用自己背一个js解释器),直接执行js脚本并获取返回值。js层则是界面的布局与事件的响应处理。
启动 MoviesActivity.java 1
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.java 1
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.java 1
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.java 1
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.java 1
2
3
4
5
class JavaScriptModuleRegistry {
private final HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> mModuleInstances;
...
}
js模块注册表
ToastModule.java 1
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.java 1
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.java 1
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.java 1
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.java 1
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.cpp 1
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.js 1
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.js 1
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.js 1
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.js 1
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++返回给javaBridge.cpp 1
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.cpp 1
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.java 1
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.java 1
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开发新的思路吧。