React Native是怎么在Android上跑起来的

(源码版本:0.34,新版本(0.48)基本流程是不变的,建议跟着源码看看,哪个版本的倒影响不大)
这篇简单刨析一下React Native是怎么在Android上跑起来的,会从下面几个方面说说。

  • 启动流程
  • 通信机制
  • 事件驱动
  • 渲染原理
  • 脚本执行

启动流程

React NativeAndroid上启动是从ReactRootView.startReactApplication触发的,而ReactRootView是继承FrameLayout的,所以React NativeAndroid的操作都是在这个View中进行的。

startReactApplication(ReactInstanceManager reactInstanceManager,String moduleName,@Nullable Bundle launchOptions)

这个方法参数第一个ReactInstanceManager,实现是XReactInstanceManagerImpl,可以理解在应用层对RN的配置都是对这个类操作实现的。moduleName是要启动的RNComponentname,是在jsAppRegistry.registerComponent('xxx',() => App);定义的。最后的launchOptions是传过去的参数,可以在jsComponentprops中获取。

下一步到了mReactInstanceManager.createReactContextInBackground();是在后台线程中创建RNReactContext上下文对象,然后到

//JavaScriptExecutor 默认就是jsc,如果的debug在chrome上时候,就是v8。
//JSBundleLoader 有AssetLoader FileLoader CachedBundleFromNetworkLoader RemoteDebuggerBundleLoader 从不同的地方加载bundle
 private void recreateReactContextInBackground(
      JavaScriptExecutor.Factory jsExecutorFactory,JSBundleLoader jsBundleLoader) {
    UiThreadUtil.assertOnUiThread();

    ReactContextInitParams initParams =
        new ReactContextInitParams(jsExecutorFactory,jsBundleLoader);
    if (mReactContextInitAsyncTask == null) {
      // No background task to create react context is currently running,create and execute one.
      mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
      mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,initParams);
    } else {
      // Background task is currently running,queue up most recent init params to recreate context
      // once task completes.
      mPendingReactContextInitParams = initParams;
    }
  }

主要的创建工作就转移到了ReactContextInitAsyncTask这个AsyncTask里面,

@Override
    protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
      ....
       return Result.of(createReactContext(jsExecutor,params[0].getJsBundleLoader()));
      ....
    }

  private ReactApplicationContext createReactContext(
      JavaScriptExecutor jsExecutor,JSBundleLoader jsBundleLoader) {
    ...
     NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();//NativeModule的注册表
    JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();//jsModules的注册表
    ...打包定义的各种modules到上面的注册表...

        //创建关键的CatalystInstanceImpl
        CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
        ...
        catalystInstance = catalystInstanceBuilder.build();
    ....
    //扔到js线程中加载js脚本
    catalystInstance.getReactQueueConfiguration().getJSQueueThread().callOnQueue(
        new Callable<Void>() {
          @Override
          public Void call() throws Exception {
            //让reactContext持有catalystInstance
            reactContext.initializeWithInstance(catalystInstance);
            ...
            catalystInstance.runJSBundle();
            return null;
          }
        }).get();
    }

CatalystInstanceImpl的构造函数中有

...
//native C++方法,用来初始化JNI相关状态然后返回mHybridData。具体在 OnLoad.cpp 的 JSCJavaScriptExecutorHolder 类中
mHybridData = initHybrid();
...
//初始化线程环境,包括和主线程绑定,JS线程,Native线程创建。
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
        ReactQueueConfigurationSpec,new NativeExceptionHandler());
...
initializeBridge(
      new BridgeCallback(this),//CatalystInstanceImpl内部类,用于native对java的一些回调
      jsExecutor,//jsc
      mReactQueueConfiguration.getJSQueueThread(),//js线程队列
      mReactQueueConfiguration.getNativeModulesQueueThread(),//native线程队列
      mJavaRegistry.getModuleRegistryHolder(this));//nativemodules注册表
    mMainExecutorToken = getMainExecutorToken();//貌似是用于切换jsExecutor的标记,后面版本删掉了。

然后就进入到了cpp层的CatalystInstanceImpl.cppinitializeBridge方法

void CatalystInstanceImpl::initializeBridge(
    jni::alias_ref<ReactCallback::javaobject> callback,// This executor is actually a factory holder.
    JavaScriptExecutorHolder* jseh,jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,ModuleRegistryHolder* mrh) {

  instance_->initializeBridge(folly::make_unique<JInstanceCallback>(callback),jseh->getExecutorFactory(),folly::make_unique<JMessageQueueThread>(jsQueue),folly::make_unique<JMessageQueueThread>(moduleQueue),mrh->getModuleRegistry());
}

然后有委托给了Instance.cppinitializeBridge方法

void Instance::initializeBridge(
    std::unique_ptr<InstanceCallback> callback,std::shared_ptr<JSExecutorFactory> jsef,std::shared_ptr<MessageQueueThread> jsQueue,std::unique_ptr<MessageQueueThread> nativeQueue,std::shared_ptr<ModuleRegistry> moduleRegistry) {
  callback_ = std::move(callback);

  //在js线程中包装nativeQueue和创建nativeToJsBridge_,后者在双向bridge起作用,不要仅仅看名字,内部还有一个JsToNativeBridge
  jsQueue->runOnQueueSync(
    [this,&jsef,moduleRegistry,jsQueue,nativeQueue=folly::makeMoveWrapper(std::move(nativeQueue))] () mutable {
      nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
          jsef.get(),nativeQueue.move(),callback_);
    });
  CHECK(nativeToJsBridge_);
}

到这就看没了,再回到上面的catalystInstance.runJSBundle();FileLoader为例,最终走到native void loadScriptFromFile(String fileName,String sourceURL);进入CatalystInstanceImpl.cpp进而委托给Instance.cpp。预警。。下面是一大片的cpp代码

void Instance::loadScriptFromFile(const std::string& filename,const std::string& sourceURL) {
  ...检测文件合法性等...
  loadScriptFromString(std::move(buf),sourceURL);
}

void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,std::string sourceURL) {
  callback_->incrementPendingJSCalls();//这个callback就是java层的CatalystInstanceImpl的BridgeCallback这个内部类。
  ...
  nativeToJsBridge_->loadApplicationScript(std::move(string),std::move(sourceURL));
}

void NativeToJsBridge::loadApplicationScript(std::unique_ptr<const JSBigString> script,std::string sourceURL) {
  m_mainExecutor->loadApplicationScript(std::move(script),std::move(sourceURL));
}

void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script,std::string sourceURL) throw(JSException) {
    ...
     //使用webkit JSC去真正解释执行Javascript了!
     evaluateScript(m_context,jsScript,jsSourceURL);
     //绑定桥,核心是通过getGlobalObject将JS与C++通过webkit JSC bind
     bindBridge(); 
     flush();
    }

void JSCExecutor::bindBridge() throw(JSException) {
  ...下面都是通过jsc 获取js的一下属性,方法等...
  auto global = Object::getGlobalObject(m_context);
  auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
...
  auto batchedBridge = batchedBridgeValue.asObject();
  m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
  m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
  //这个比较重要 获取MessageQueue.js的flushedQueue 下面就用到
  m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
}

//这个下面js->native的时候还会提到
void JSCExecutor::flush() {
  ...真的烦,绕来绕去  m_flushedQueueJS看上面
  callNativeModules(m_flushedQueueJS->callAsFunction({}));
}

void JSCExecutor::callNativeModules(Value&& value) {
    ...
  try {
    auto calls = value.toJSONString();
    //class JsToNativeBridge : public react::ExecutorDelegate
    m_delegate->callNativeModules(*this,std::move(calls),true);
  } catch (...) {
   ...
  }
}

  void callNativeModules(
      JSExecutor& executor,std::string callJSON,bool isEndOfBatch) override {
    ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);
    m_nativeQueue->runOnQueue([this,token,callJSON=std::move(callJSON),isEndOfBatch] {
      for (auto& call : react::parseMethodCalls(callJSON)) {
        //快完了  这个是ModuleRegistry.cpp 是在initializeBridge间接创建包装nativemodule的
        m_registry->callNativeMethod(
          token,call.moduleId,call.methodId,std::move(call.arguments),call.callId);
      }
      if (isEndOfBatch) {
        //又见到了这个callback
        m_callback->onBatchComplete();
        m_callback->decrementPendingJSCalls();
      }
    });
  }

void ModuleRegistry::callNativeMethod(ExecutorToken token,unsigned int moduleId,unsigned int methodId,folly::dynamic&& params,int callId) {
 ...

  modules_[moduleId]->invoke(token,methodId,std::move(params));

}

看到最后一句就是要去调用nativeModule里面的方法了,具体在ModuleRegistryHolder.cppJavaNativeModule类和NewJavaNativeModule类,对应JavaJavaModuleWrapper.java,就是jni调用。

说到这里,现在只完成了bridge环境的初步搭建,把jsbundle扔到jsc里面,还没真正拉起React Native应用。还是回到上面那个AsyncTaskonPostExecute方法。看看执行完这么一大堆准备代码之后,是怎么拉起来整个应用的。

@Override
    protected void onPostExecute(Result<ReactApplicationContext> result) {
      ....
       setupReactContext(result.get());
    }

    private void setupReactContext(ReactApplicationContext reactContext) {
    ...各种listener回调,通知birdge就绪,reactContext创建完成
    for (ReactRootView rootView : mAttachedRootViews) {
      attachMeasuredRootViewToInstance(rootView,catalystInstance);
    }
    ...各种listener回调,通知birdge就绪,reactContext创建完成
  }


private void attachMeasuredRootViewToInstance(ReactRootView rootView,CatalystInstance catalystInstance) {
   ....
    UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
    int rootTag = uiManagerModule.addMeasuredRootView(rootView);
    rootView.setRootViewTag(rootTag);
    @Nullable Bundle launchOptions = rootView.getLaunchOptions();
    WritableMap initialProps = Arguments.makeNativeMap(launchOptions);
    String jsAppModuleName = rootView.getJSModuleName();

    WritableNativeMap appParams = new WritableNativeMap();
    appParams.putDouble("rootTag",rootTag);
    appParams.putMap("initialProps",initialProps);
    //真正拉起react native 的地方
    catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName,appParams);
  }

再来详细说一下最后一句,(大量代码预警)

@Override
  public <T extends JavaScriptModule> T getJSModule(ExecutorToken executorToken,Class<T> jsInterface) {
    //进入JSModuleRegistry中
    return Assertions.assertNotNull(mJSModuleRegistry)
        .getJavaScriptModule(this,executorToken,jsInterface);
  }

  //JavaScriptModuleRegistry.java
   public synchronized <T extends JavaScriptModule> T getJavaScriptModule(
    CatalystInstance instance,ExecutorToken executorToken,Class<T> moduleInterface) {
    HashMap<Class<? extends JavaScriptModule>,JavaScriptModule> instancesForContext =
        mModuleInstances.get(executorToken);
    if (instancesForContext == null) {
      instancesForContext = new HashMap<>();
      //缓存一下 方便后面再使用
      mModuleInstances.put(executorToken,instancesForContext);
    }
    JavaScriptModule module = instancesForContext.get(moduleInterface);
    if (module != null) {
      //命中缓存 直接返回
      return (T) module;
    }
    JavaScriptModuleRegistration registration =
      ...
      //很明显 动态代理 重点关注JavaScriptModuleInvocationHandler的invoke方法
    JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
        moduleInterface.getClassLoader(),new Class[]{moduleInterface},new JavaScriptModuleInvocationHandler(executorToken,instance,registration));
    instancesForContext.put(moduleInterface,interfaceProxy);
    return (T) interfaceProxy;
  }

  @Override
    public @Nullable Object invoke(Object proxy,Method method,@Nullable Object[] args) throws Throwable {
      ....
      //又跑到了CatalystInstanceImpl.java中。。。然后又桥接到了CatalystInstanceImpl.cpp中,同样会调用instance的对应方法,直接看吧
      mCatalystInstance.callFunction(
        executorToken,mModuleRegistration.getName(),method.getName(),jsArgs
      );
      return null;
    }

void Instance::callJSFunction(ExecutorToken token,std::string&& module,std::string&& method,folly::dynamic&& params) {
  callback_->incrementPendingJSCalls();//这个回调不多说
  //....接着跟吧
  nativeToJsBridge_->callFunction(token,std::move(module),std::move(method),std::move(params));
}  

//又会进入executor->callFunction(module,method,arguments);
void JSCExecutor::callFunction(const std::string& moduleId,const std::string& methodId,const folly::dynamic& arguments) {
    ....
  auto result = [&] {
    try {
      //被桥接到MessageQueue.js的callFunctionReturnFlushedQueue方法
      return m_callFunctionReturnFlushedQueueJS->callAsFunction({
        Value(m_context,String::createExpectingAscii(moduleId)),Value(m_context,String::createExpectingAscii(methodId)),Value::fromDynamic(m_context,std::move(arguments))
      });
    } catch (...) {
      std::throw_with_nested(
        std::runtime_error("Error calling function: " + moduleId + ":" + methodId));
    }
  }();
  //顺便还会调用一下native的 这个会在后面再说一下
  callNativeModules(std::move(result));
}

 callFunctionReturnFlushedQueue(module,args) {
        guard(() => {
            //执行js的function
            this.__callFunction(module,args);
            this.__callImmediates();
        });
        //取出积攒在queue中的action返回给上面的,最终在java中执行
        return this.flushedQueue();
    }

__callFunction(module: string,method: string,args: any) {
       ...
       //根据module名,方法名和参数执行js方法
        const result = moduleMethods[method].apply(moduleMethods,args);
        return result;
    }
    //那什么时候把js的module注册到moduleMethods中呢
//AppRegistry.js
BatchedBridge.registerCallableModule(
  'AppRegistry',AppRegistry
);
//BatchedBridge是啥?
const BatchedBridge = new MessageQueue(
  () => global.__fbBatchedBridgeConfig,serializeNativeParams
);
registerCallableModule(name,methods) {
        this._callableModules[name] = methods;
    }

这里就执行了AppRegistry.js的的runApplication方法。

runApplication: function(appKey: string,appParameters: any): void {
   ...
    runnables[appKey].run(appParameters);
  },//而runnables是在什么时候被添加的??下面

  registerComponent: function(appKey: string,getComponentFunc: ComponentProvider): string {
    runnables[appKey] = {
      run: (appParameters) =>
        renderApplication(getComponentFunc(),appParameters.initialProps,appParameters.rootTag)
    };
    return appKey;
  },//而registerComponent什么时候被调用的就不用说了吧

到此真正执行到了js脚本,开始执行Component的逻辑渲染,最终映射到NativeView上。后面会再详细说渲染的原理。同时会发现在 JSCExecutor 中每次 Java 调用 JS 之后会进行 Java 端的一个回调(从 JS 层的 MessageQueue.js 中获得累积的 JS Call)。

通信机制

上面关于java->js已经体现的差不多了,实质就是 JavaJS 端都准备好一个 Module 映射表,然后当 Java 端调用 JS 代码时 Java 端通过查表动态代理创建一个与 JS 对应的 Module 对象,当调用这个 Module 的方法时 Java 端通过动态代理的 invoke 方法触发 C++ 层,层层调用后通过 JSCExecutor 执行 JS 端队列中的映射查表找到 JS 端方法进行调用;js->java的调用会在渲染原理里面提到。

简单画了个图

渲染原理

现在以一个Image如何渲染到Native为例,说一下简单的流程。
当执行js的脚本时候,是不知道nativeModule的注册表的,因为nativeModule的注册表只保存在javacpp端,并没有直接传递到js端。所有当执行到

import {
    Image,} from 'react-native';

这时候js并不知道Image是什么,然后看一下代码

const ReactNative = {
    ...
    get Image() { return require('Image'); },...
}
...
module.exports = ReactNative;

//Image.android.js

var NativeModules = require('NativeModules');

//NativeModules.js

const NativeModules = {};
Object.keys(RemoteModules).forEach((moduleName) => {
  Object.defineProperty(NativeModules,moduleName,{
    configurable: true,enumerable: true,get: () => {
      let module = RemoteModules[moduleName];
      if (module && typeof module.moduleID === 'number' && global.nativeRequireModuleConfig) {
      //nativeRequireModuleConfig映射到JSCExecutor.cpp
        const config = global.nativeRequireModuleConfig(moduleName);
        module = config && BatchedBridge.processModuleConfig(config,module.moduleID);
        RemoteModules[moduleName] = module;
      }
      Object.defineProperty(NativeModules,{
        configurable: true,value: module,});
      return module;
    },});
});

module.exports = NativeModules;

//cpp
JSCExecutor::nativeRequireModuleConfig->JsToNativeBridge::getModuleConfig->ModuleRegistry::getConfig

folly::dynamic ModuleRegistry::getConfig(const std::string& name) {

...
  NativeModule* module = modules_[it->second].get();
...
  //最终反射调用JavaModuleWrapper.java的getConstants
  folly::dynamic constants = module->getConstants();
...
  //最终反射调用JavaModuleWrapper.java的getMethods
  //返回对应module中所有@ReactMethod注解的方法
  std::vector<MethodDescriptor> methods = module->getMethods();

//modules_在哪赋值?
//ModuleRegistryHolder.cpp构造函数,这个类上面有提到,回去看看
//registry_ = std::make_shared<ModuleRegistry>(std::move(modules));
}

然后返回到NativeModules.js中,BatchedBridge.processModuleConfig
->_genModule->_genMethod。进一步处理一下。所以到现在,js获取到了Image这个module中所有方法和属性。

然后当调用Image中相关方法时候,其实就是调用上面_genMethod中的方法,在这个方法中,分promisesync其他调用类型,最终都是调用了

__nativeCall(module,params,onFail,onSucc) {
...
        this._queue[MODULE_IDS].push(module);
        this._queue[METHOD_IDS].push(method);
        this._queue[PARAMS].push(preparedParams);
...
//如果5ms内有多个方法调用就先待在队列里防止过高频率,否则调用C++的nativeFlushQueueImmediate方法
 if (global.nativeFlushQueueImmediate &&
        now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
      global.nativeFlushQueueImmediate(this._queue);
      this._queue = [[],[],this._callID];
      this._lastFlush = now;
    }
}

上面把MODULE_IDSMETHOD_IDSPARAMS放到queue中,等待java的调用,至于什么时候会触发java的调用和为什么要这么设计,会在下面的事件驱动解释。调用JSCExecutor::flush()。还有就是直接调用cppnativeFlushQueueImmediate,最终这两种方式都是调用了callNativeModules,这个上面也说了,不再赘述啦。

下面再说一下Nativeview创建过程,这个过程中Viewtag起标记View的作用,从java拉起React NativeattachMeasuredRootViewToInstance方法中可以看到

appParams.putDouble("rootTag",initialProps);

rootTag通过bridge带到了js端,js执行React逻辑后,要创建一个NativeView,同时也把这个rootTag带到java层,让java层知道,创建完一个View要添加到哪个根布局上。

这个rootTag的生成是有规则的,在UIManagerModule.addMeasuredRootView的时候会生成RootViewTag

final int tag = mNextRootViewTag;//默认是1
    mNextRootViewTag += ROOT_VIEW_TAG_INCREMENT;//10

也就是默认的rootTag是1,后面每多创建一个+10,也就是类似1,11,21这样都是根布局的tag

再通过这个rootTagjs的传递简单说一下React.js的创建组件逻辑。从前面可以知道,拉起js后执行AppRegistry.js ::runApplication,进而执行到了renderApplication(getComponentFunc(),appParameters.rootTag)这个方法。这里可以看到从java传过来的两个参数,其中一个就是rootTag,这里默认就一个根布局,这里的rootTag==1,进而到了renderApplication.js

ReactNative.render(
    <AppContainer> <RootComponent  {...initialProps} rootTag={rootTag} /> </AppContainer>,rootTag );

这里的AppContainer也是一个组件,是包裹在根布局的外面,用于debug的红盒等工具布局。再到了

//ReactNative.js
var render = function (element,mountInto,callback) {
return ReactNativeMount.renderComponent(element,callback);
};

这里面的逻辑快到React的一些处理,这里不多赘述,其实还有很多关于React Native的处理,暂时忽略,分支太多太繁琐。简单说一下React Native组件可以分为两种

元组件:框架内置的,可以直接使用的组件。例如:View、Image等。它在React Native中用ReactNativeBaseComponent来描述。
复合组件:用户封装的组件,一般可以通过React.createClass()来构建,提供render()方法来返回渲染目标。它在React Native中用ReactCompositeComponent来描述。

具体组合的逻辑基本都在上面连个类里面。下面来到ReactNativeBaseComponent.jsmountComponent,根据上面的提示是可以跟到这里的。只挑简单的看,看这个方法里面的

var tag = ReactNativeTagHandles.allocateTag();//给每个view生成一个唯一的tag
...
UIManager.createView(tag,this.viewConfig.uiViewClassName,nativeTopRootTag,updatePayload);

//ReactNativeTagHandles.js
allocateTag: function () {
    //排除已经给分配给rootTag的 类似1,11,21
    //下面的就是简单的自增,初始化是1
    while (this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) {
      ReactNativeTagHandles.tagCount++;
    }
    var tag = ReactNativeTagHandles.tagCount;
    ReactNativeTagHandles.tagCount++;
    return tag;
  },

看名字也知道这里就到了创建View的地方,还有另外两个方法和这个差不多的,用来操作View,分别的updateViewmanageChildrenUIManager通过bridge可以映射到javaUIManagerModule.java,可以在duiyiing这个类里面找到对应的用@ReactMethod注解的方法,这个注解是干啥的,看上面有提到。这里只看createView

@ReactMethod
  //创建view的tag,对应native的组件类名,要加入的根布局tag,创建view需要的参数
  public void createView(int tag,String className,int rootViewTag,ReadableMap props) {
    mUIImplementation.createView(tag,className,rootViewTag,props);
  }

UIImplementation.java中把要创建的view包装成CSSNode,用于后面的在CssLayout中布局。然后会包装成一个CreateViewOperation加入到UIViewOperationQueue.javaArrayDeque<UIOperation> mNonBatchedOperations这个队列中。最后还是通过GuardedChoreographerFrameCallback这个垂直同步的回调中出队,执行。关于事件驱动还是看下面。还有 updateview setchilderen就不说了,很复杂。

事件驱动

在说React Native的事件驱动之前,先看一下这几篇
Android图形显示系统(一)
React Native 分析(二)事件驱动
Android中的SurfaceFlinger和Choreographer
了解一下垂直同步和在Android上的Choreographer,正因为React Native使用了Choreographer这个类,而这个类是在4.1加入的,所以RN-Android的最低兼容是4.1,而weex是最低兼容到4.0,是在4.0使用了handler延时来模拟垂直同步的效果。当然这也是老版本Android的做法。这也是为啥总是吐槽Android显得很卡,当然在5.0又引入了renderThread就更上了一个台阶,还有Android的属性动画也是靠这个驱动的。

下面简单贴一下Choreographer的注释,看看为啥跨平台的框架都会用到这个类

However,there are a few cases where you might want to use the functions of thechoreographer directly in your application. Here are some examples.

  • If your application does its rendering in a different thread,possibly using GL,or does not use the animation framework or view hierarchy at all and you want to ensure that it is appropriately synchronized with the display,then use
    {@link Choreographer#postFrameCallback}.
  • … and that’s about it.
  • Each {@link Looper} thread has its own choreographer. Other threads can post callbacks to run on the choreographer but they will run on the {@link Looper}to which the choreographer belongs.

    再看一下postFrameCallback注释

    Posts a frame callback to run on the next frame.The callback runs once then is automatically removed.

    React Native的使用主要在EventDispatcher的内部类private class ScheduleDispatchFrameCallback implements Choreographer.FrameCallbackReactChoreographer与它的内部类private class ReactChoreographerDispatcher implements Choreographer.FrameCallback,还有用于view或者动画的就不说了。

    现在举个例子,点击一下view,这个事件是怎么传递的,点击事件肯定发生在java端。在ReactRootViewdispatchJSTouchEvent方法

    ...
      EventDispatcher eventDispatcher = reactContext.getNativeModule(UIManagerModule.class)
          .getEventDispatcher();
        mJSTouchDispatcher.handleTouchEvent(event,eventDispatcher);
    
    //JSTouchDispatcher.java
      public void handleTouchEvent(MotionEvent ev,EventDispatcher eventDispatcher) {
        //这里面分为down,up move 等事件类别
    
         mTargetTag = TouchTargetHelper.findTargetTagAndCoordinatesForTouch(
            ev.getX(),ev.getY(),mRootViewGroup,mTargetCoordinates,null);
          eventDispatcher.dispatchEvent(
            TouchEvent.obtain(
              mTargetTag,TouchEventType.START,ev,mGestureStartTime,mTargetCoordinates[0],mTargetCoordinates[1],mTouchEventCoalescingKeyHelper));
    
    }

    最终包装成一个TouchEvent调用eventDispatcher.dispatchEvent,这里面主要是

    mEventStaging.add(event);//ArrayList<Event>

    把事件添加到一个待发送的列表里面。那什么是去处发送?是在ScheduleDispatchFrameCallback.doFrame

    @Override
        public void doFrame(long frameTimeNanos) {
        ....
          moveStagedEventsToDispatchQueue();
        ...
           mReactContext.runOnJSQueueThread(mDispatchEventsRunnable);
    
        }

    调用moveStagedEventsToDispatchQueue在这个方法里面会对event再做一些处理,例如压缩,合并事件等,然后又把处理完的事件放到Event[] mEventsToDispatch = new Event[16];中。而在DispatchEventsRunnablerun方法中

    @Override
        public void run() {
         for (int eventIdx = 0; eventIdx < mEventsToDispatchSize; eventIdx++) {
                Event event = mEventsToDispatch[eventIdx];
                ....
                event.dispatch(mRCTEventEmitter);
                event.dispose();
                ...
                }
        }
    
    ->TouchEvent.dispatch->TouchesHelper.sendTouchEvent->rctEventEmitter.receiveTouches(
            type.getJSEventName(),pointers,changedIndices);

    RCTEventEmitter extends JavaScriptModule这个就是走上面的java->js的路子,动态代理->cpp->flush()->….

    简单点就是getJSModule后对js的方法调用都会触发上面MessageQueue.js的出队

    脚本执行

    这里简单说说React Nativejs引擎选择,都是webkitJSC,在iOS上是内置的,在Android上则是引入了一个完整的JSC,这也是为什么AndroidRN会大这么多的很重要的原因,至于为什么要引入一个完整的JSC而不是使用内置的js引擎,Android 4.4之前的android系统浏览器内核是WebKitAndroid4.4系统浏览器切换到了Chromium(内核是Webkit的分支Blink)。在Android平台已经启用V8作为JS引擎,Android 4.0以后只用到了JavaScriptCore中的WTF(Web Template Library)部分代码。

    至于为啥不都使用V8,这个都是iOS的锅,看看chromeiOS上就是个WebView套个壳。。。

    还有其他的跨平台框架,例如weex,在Android上使用的是V8。现在网上也有对RNAndroid上移植的V8版本。
    onesubone/react-native-android-v8
    React Native Android V8接入
    这个是基于0.46的版本,还是可以跑起来的,但是RN的速度瓶颈貌似并不在js引擎。。

    最后再贴一下简单画的思维导图吧

    思维导图

    参考:

    facebook/react-native
    React Native Android 源码框架浅析(主流程及 Java 与 JS 双边通信)
    ReactNative源码篇
    吟游雪人

    ps:

    因为本人能力实在有限,上面很多都是连蒙带猜,算是个笔记性质的流水账,有用就看看,没用就算了,欢迎指出错误。

    pps

    这篇本该在两星期之前完成的工作,一直拖到了现在。(一是因为懒),二是因为不知道该怎么更好的表述出来,因为一直贴代码体验实在是不好。(虽然现在还是这样的,但是源码分析的不贴代码怎么写)。但是感觉再不写点出来,过段时间又忘了,索性写出来算了,也不管效果了。。。凑合看吧。

    相关文章

    react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
    我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
    我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...
    react 本身提供了克隆组件的方法,但是平时开发中可能很少使...
    mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接...
    我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc ...