React-Native series Android-Principles of communication between Native and Javascript (III)

Source: Internet
Author: User

React-Native series Android-Principles of communication between Native and Javascript (III)

The previous two blogs have analyzed in detailNativeAndJavascriptThe communication process can meet the needs of most scenarios.NativeAndJavascriptBut there is still a sound situation.

For exampleJavascriptReal-time retrieval of LayerNativeYou needNativePassive OrientationJavascriptLayer communication. This process is different from communication in Article 1NativeProactiveJavascriptLayer-7 communication, this blog will study such a passive callback process!

First, analyze from a common scenario.

Assume that the front-end developerJavascriptTo obtainAPPSuchAPPWhether it is in the foreground (Active), Or back-end (Background). There are two possible implementation methods:
1,NativeInAPPCallCallFunctionSend the latest statusJavascriptLayerJavascriptCache, so that developers can directly use the cached value to obtain the status.
2. Front-end developersJavascriptTo obtain the statusNativeThe client initiates a communication request to obtain the status.NativeThis status is returned as a communication responseJavascriptLayer.

Both solutions have their own application scenarios.React-NativeAre implemented accordingly. The first implementation is relatively simple for developers. It is a completely synchronous process to directly retrieve the cache value. Second Implementation directionNativeInitiate a Communication Request and waitNativeIs an asynchronous process.

The principle of the first solution is as follows:React-Native series Android-communication between Native and Javascript (I)This article focuses on the Implementation Principles of the second solution.

1. JavaScriptRequest

NativeAndJavaScriptThe communication is composedNativeAnd thenJavaScriptResponse,JavaScriptCannot forwardNativeActively initiate communication. So,JavaScriptHow to forwardNativeInitiate a communication request?

As mentioned in the previous blog,JavaScriptResponseNativeBy packaging the response dataJSONFormat, and thenFlushedQueue ()ReturnBridgeReturnNative. IfJavaScriptAdd the ID of the Communication Request to the response information.NativeWhen parsing the response information, it was found thatJavaScriptAnd thenNativeTo respond to this request.JavaScriptRequestNative?

Step 1: Add a JavaScript Communication Request to the response message returned to Native

SimilarlyJavascript.APPThe sample code is as follows:

var NativeModules = require('NativeModules');var RCTAppState = NativeModules.AppState;var logError = require('logError');RCTAppState.getCurrentAppState(  (appStateData) => {     console.log('dev', 'current state: ' + appStateData.app_state);  },  logError);

Analyzed in the previous blogNativeModulesIn the past and present, it is a class that is dynamically initialized (For details, refer to the previous article about the communication principle between Native and Javascript (II), which is skipped here ),RCTAppState. getCurrentAppStateActually, the call isMessageQueue. jsThe following code:

function(...args) {   let lastArg = args.length > 0 ? args[args.length - 1] : null;   let secondLastArg = args.length > 1 ? args[args.length - 2] : null;   let hasSuccCB = typeof lastArg === 'function';   let hasErrorCB = typeof secondLastArg === 'function';   hasErrorCB && invariant(hasSuccCB, 'Cannot have a non-function arg after a function arg.');   let numCBs = hasSuccCB + hasErrorCB;   let onSucc = hasSuccCB ? lastArg : null;   let onFail = hasErrorCB ? secondLastArg : null;   args = args.slice(0, args.length - numCBs);   return self.__nativeCall(module, method, args, onFail, onSucc);};

ParametersArgsThere are two concrete expressions. One isLambdaExpression callback function. One isLogError, AllFunctionType. ResolutionLastArgVariable refersLogError,SecondLastArgA variable is a callback function.

Therefore_ NativeCallTwo parameters passed during the FunctionOnFailAndOnSuccThe callback function andLogError. Here is obviouslyReact-NativeNameBugI almost thought it was the reverse resolution of the two variables, but it didn't affect the entire process (the reason isNativeWhen parsing parameters in the Code, the default value isOnSuccIn front, it is reversed and will be analyzed later ).

Next let's take a look_ NativeCall

__nativeCall(module, method, params, onFail, onSucc) {    if (onFail || onSucc) {      ...      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);    ...  }

This. _ queueIs used to save the response.NativeHere we mainly look at the dataIfThe judgment logic.

This. _ callbackIDYesThis. _ callbacksThe index of the set to identify the callback function, and the index will be placed inParamsPassedNativeTerminal,NativeThe index is returned when the client responds.JavascriptEnd, this wayJavascriptYou can find the data stored inThis. _ callbacksThe callback function in the set. So,This. _ callbackIDYesJavascriptRequestNative.

Step 2: How does Native respond to Javascript

Another step in the middleFlushedQueue ()DirectionBridgeFor the transfer process of the layer, refer to the previous article. Skip this section.

Analyzed in the previous blogNativeProcess fromJavascriptThe response information isModuleID + methodIDMap to specificNativeModuleComponent method, parse the parameters, and finally passInvokeReflection Method to complete the call.

In the example, obtainAPPThe component in the current status isNativeCorrespondingNativeModuleClass isAppStateModule. The method mapped to isGetCurrentAppStateIt has twoCallbackType parameter.

Let's see.NativeModuleAnalysisCallbackType parameter code, which is located in its parent classCom. facebook. react. bridge. BaseJavaModule. javaMedium

  static final private ArgumentExtractor
  
    ARGUMENT_EXTRACTOR_CALLBACK =      new ArgumentExtractor
   
    () {        @Override        public @Nullable Callback extractArgument(            CatalystInstance catalystInstance, ReadableNativeArray jsArguments, int atIndex) {          if (jsArguments.isNull(atIndex)) {            return null;          } else {            int id = (int) jsArguments.getDouble(atIndex);            return new CallbackImpl(catalystInstance, id);          }        }      };
   
  
private ArgumentExtractor[] buildArgumentExtractors(Class[] paramTypes) {      ArgumentExtractor[] argumentExtractors = new ArgumentExtractor[paramTypes.length];      for (int i = 0; i < paramTypes.length; i += argumentExtractors[i].getJSArgumentsNeeded()) {        Class argumentClass = paramTypes[i];        ...        if (argumentClass == Callback.class) {          argumentExtractors[i] = ARGUMENT_EXTRACTOR_CALLBACK;        }        ...      }      return argumentExtractors;    }

ForCallbackType parameter. The extracted parameter isARGUMENT_EXTRACTOR_CALLBACK, In itsExtractArgumentMethodJavascriptFrom the terminalCallbackID, ConstructorCallbackImplObject. And this constructedCallbackImplObject isInvokeReflectionGetCurrentAppStateParameters in the method.

Next let's take a look at the reflectedGetCurrentAppStateMethod, located inCom. facebook. react. modules. appstate. AppStateModule. java

public class AppStateModule extends ReactContextBaseJavaModule        implements LifecycleEventListener {  public static final String APP_STATE_ACTIVE = "active";  public static final String APP_STATE_BACKGROUND = "background";  private String mAppState = "uninitialized";  public AppStateModule(ReactApplicationContext reactContext) {    super(reactContext);  }  @Override  public String getName() {    return "AppState";  }  @Override  public void initialize() {    getReactApplicationContext().addLifecycleEventListener(this);  }  @ReactMethod  public void getCurrentAppState(Callback success, Callback error) {    success.invoke(createAppStateEventMap());  }  @Override  public void onHostResume() {    mAppState = APP_STATE_ACTIVE;    sendAppStateChangeEvent();  }  @Override  public void onHostPause() {    mAppState = APP_STATE_BACKGROUND;    sendAppStateChangeEvent();  }  ...  private WritableMap createAppStateEventMap() {    WritableMap appState = Arguments.createMap();    appState.putString("app_state", mAppState);    return appState;  }  ...}

WhenActivityWhen the lifecycle changes, the status will be updatedMAppState,CreateAppStateEventMap ()SetMAppStateEncapsulated inNative-BridgeInter-passWritableMapObject. ThenSuccess. invoke (), And thisCallbackTypeSuccessThe preceding ParameterARGUMENT_EXTRACTOR_CALLBACKConstructedCallbackImplObject. The memory stores the identifier used for callback.CallbackID.

So, let's seeCallbackImplOfInvokeMethod, the code inCom. facebook. react. bridge. CallbackImpl. java

public final class CallbackImpl implements Callback {  private final CatalystInstance mCatalystInstance;  private final int mCallbackId;  public CallbackImpl(CatalystInstance bridge, int callbackId) {    mCatalystInstance = bridge;    mCallbackId = callbackId;  }  @Override  public void invoke(Object... args) {    mCatalystInstance.invokeCallback(mCallbackId, Arguments.fromJavaArgs(args));  }}

ItsInvokeThe method is called again.CatalystInstance. invokeCallbackWe know from the previous two blog postsCatalystInstanceYesNativeDirectionJavascriptCommunication entry, which is obvious hereCatalystInstance. invokeCallbackYesNativePairJavascript. It contains the identifierCallbackIDAnd content dataMAppState.

InCatalystInstanceImplementation classCatalystInstanceImplInternal and passReactBridgeCallJNIThis is the same
React-Native series Android-communication between Native and Javascript (I)InCallFunctionThe principle is the same.

public class CatalystInstanceImpl implements CatalystInstance {   ...   public void invokeCallback(final int callbackID, final NativeArray arguments) {       ...       Assertions.assertNotNull(mBridge).invokeCallback(callbackID, arguments);       ...   }   ...}

Step 3: Bridge transit

Passed in the previous stepJNICalledInvokeCallbackMethod, which has two parameters:CallbackIDAndArguments.CallbackIDYes fromJavascriptCommunication callback ID of the client,ArgumentsYesNativeResponseJavascriptRequest content.BridgeIs to transfer the two parametersJavascript.

BridgeThe call entry for the layer isReact \ jni \ OnLoad. cppLet's take a look.InvokeCallbackMethod

static void invokeCallback(JNIEnv* env, jobject obj, JExecutorToken::jhybridobject jExecutorToken, jint callbackId,                           NativeArray::jhybridobject args) {  auto bridge = extractRefPtr
  
   (env, obj);  auto arguments = cthis(wrap_alias(args));  try {    bridge->invokeCallback(      cthis(wrap_alias(jExecutorToken))->getExecutorToken(wrap_alias(jExecutorToken)),      (double) callbackId,      std::move(arguments->array)    );  } catch (...) {    translatePendingCppExceptionToJavaException();  }}
  

The call isCountableBridgeThat isBridgeObjectInvokeCallbackMethod, the code inReact \ Bridge. cppMedium

void Bridge::invokeCallback(ExecutorToken executorToken, const double callbackId, const folly::dynamic& arguments) {  ...  auto executorMessageQueueThread = getMessageQueueThread(executorToken);  if (executorMessageQueueThread == nullptr) {    ...    return;  }  std::shared_ptr
  
    isDestroyed = m_destroyed;  executorMessageQueueThread->runOnQueue([=] () {    ...    JSExecutor *executor = getExecutor(executorToken);    if (executor == nullptr) {      ...      return;    }    ...    executor->invokeCallback(callbackId, arguments);  });}
  

InExecutorMessageQueueThreadIn the queue thread, the execution isJSExecutorOfInvokeCallbackMethod.

ContinueReact \ JSCExecutor. cpp

void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) {  std::vector
  
    call{    (double) callbackId,    std::move(arguments)  };  std::string calls = executeJSCallWithJSC(m_context, "invokeCallbackAndReturnFlushedQueue", std::move(call));  m_bridge->callNativeModules(*this, calls, true);}static std::string executeJSCallWithJSC(    JSGlobalContextRef ctx,    const std::string& methodName,    const std::vector
   
    & arguments) {  ...  // Evaluate script with JSC  folly::dynamic jsonArgs(arguments.begin(), arguments.end());  auto js = folly::to
    
     (          "__fbBatchedBridge.", methodName, ".apply(null, ",      folly::toJson(jsonArgs), ")");  auto result = evaluateScript(ctx, String(js.c_str()), nullptr);  return Value(ctx, result).toJSONString();}
    
   
  

This code andCallFunctionVery similar,ExecuteJSCallWithJSCThe second parameter is replacedInvokeCallbackAndReturnFlushedQueue.

This section generatesJavascriptThe execution statement is

__fbBatchedBridge.invokeCallbackAndReturnFlushedQueue.apply(null, jsonArgs);

JsonArgsContainsCallbackIDAndArguments,WebkitExecute this sectionJavascriptThe statement reaches the connectionJavascriptEnd.

Of courseJavascriptThere is alsoResultReturn, used to callCallNativeModulesAs a subsequent communication request, the process is exactly the same as that in the previous article!

Step 4: Javascript receives Native responses

ReferenceReact-Native series Android-communication between Native and Javascript (I), In the previous stepBridgeCreatedJavascriptExecution statement

__fbBatchedBridge.invokeCallbackAndReturnFlushedQueue.apply(null, jsonArgs);

Actually equivalent

MessageQueue.invokeCallbackAndReturnFlushedQueue.apply(null, callbackID, args);

So the execution isMessageQueue. jsOfInvokeCallbackAndReturnFlushedQueueMethod.

  invokeCallbackAndReturnFlushedQueue(cbID, args) {    guard(() => {      this.__invokeCallback(cbID, args);      this.__callImmediates();    });    return this.flushedQueue();  }

HereCbIDActuallyCallbackIDThat is, in step 1This. _ callbackID. This value is composedJavascriptPassNativeFrom now onNativeBack to Zhao!

Which of the following isThis. _ invokeCallback

  __invokeCallback(cbID, args) {    ...    let callback = this._callbacks[cbID];    ...    this._callbacks[cbID & ~1] = null;    this._callbacks[cbID |  1] = null;    callback.apply(null, args);    ...  }

This. _ callbacksSetCallbackIDStores the callback function for the index.Callback, You can useCbIDThis index isKeyExtracted. RunCallback. apply (null, args)The callback function is executed.

Also, clearThis. _ callbacksThe callback function saved in the set. Because_ NativeCallWhen the callback function is encapsulated, two callback functions are saved successively.OnFail(The index is an even number) andOnSucc(The index is odd, more than the former), and the obtainedCallbackAre you sure you wantOnFailOrOnSucc. So,CbID &~ 1Lowest position0,CbID | 1Lowest position1, RegardlessCbIDID isOnFailOrOnSuccBoth of them can be completely cleared.

ObtainAPPThe callback function in the status example is

function(appStateData){   console.log('dev', 'current state: ' + appStateData.app_state);}

App_stateThe variable value is the currentAPPAndAppStateModuleThe encapsulation of values in exactly echo

private WritableMap createAppStateEventMap() {    WritableMap appState = Arguments.createMap();    appState.putString("app_state", mAppState);    return appState;}

In this way, the entire communication process is almost complete here.

Summary

JavascriptRequestNativeReset againJavascriptThe process is as follows:

TotalJavascript> Bridge> Native> Bridge> JavascriptFive steps,CallbackIDIs the key point of the entire process.

JavascriptRequestNative, Mr ChengCallbackID, AndCallbackIDStores the callback function for the unique key.CallbackIDUploaded as the response content of the previous communication requestNativeTerminal,NativeReceived through reflectionNativeModuleAnd thenCallbackIDAnd return the processing resultJavascriptTerminal,JavascriptUseCallbackIDObtain the stored callback method and execute it.

The flowchart is as follows:

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.