1. 程式人生 > >React Native是怎麼在Android上跑起來的

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(), moduleRegistry, jsQueue, 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, method, args) {
        guard(() => {
            //執行js的function
            this.__callFunction(module, method, 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的呼叫會在渲染原理裡面提到。

簡單畫了個圖

init.png-257.4kB

渲染原理

現在以一個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, moduleName, {
        configurable: true,
        enumerable: 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, method, 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", rootTag);
    appParams.putMap("initialProps", 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.initialProps, 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, mountInto, 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的出隊

    eventloop.png-102.7kB

    指令碼執行

    這裡簡單說說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引擎。。

    最後再貼一下簡單畫的思維導圖吧

    思維導圖

    參考:

    ps:

    因為本人能力實在有限,上面很多都是連蒙帶猜,算是個筆記性質的流水賬,有用就看看,沒用就算了,歡迎指出錯誤。

    pps

    這篇本該在兩星期之前完成的工作,一直拖到了現在。(一是因為懶),二是因為不知道該怎麼更好的表述出來,因為一直貼程式碼體驗實在是不好。(雖然現在還是這樣的,但是原始碼分析的不貼程式碼怎麼寫)。但是感覺再不寫點出來,過段時間又忘了,索性寫出來算了,也不管效果了。。。湊合看吧。