1, the preface

After the previous five articles source analysis and summary, we have a clear understanding of Weex’s overall architecture and core source code. This article summarizes the details that I found useful when READING the Weex SDK source code.

Note: this article focuses on the Weex SDK source level can be used for reference details, for the general direction of reference points such as dynamic +Native ideas, a technology complete ecology and other aspects can refer to the last articleIn-depth Weex Series (8) Weex SDK Architecture Analysis.

2. Builder mode

Weex SDK is initialized before Weex is used. For Weex SDK, its auxiliary configuration classes use builder mode.

The main solution of builder mode is that the construction algorithm of each component object of a module may change, but the algorithm of each component object combined with each other is indeed stable. To sum up: the overall module build process is stable, but each step of the build can be inconsistent.

In the Weex scenario, the construction process of the Weex configuration module is stable (all modules need to provide the same capability), but each step of the construction may be different (the capability of each configuration can be varied).

For example, the Weex needs to provide basic capabilities for network requests (the construction process is stable). However, network requests can be implemented in different ways (the specific construction algorithm may change).

InitConfig config = new InitConfig.Builder().
        setImgAdapter(new WeexImageAdapter()).
        setHttpAdapter(new WeexHttpAdapter).
        setSoLoader(new WeexSoLoaderAdapter).
        build();
Copy the code

Benefits: Callers do not need to know how the building blocks are assembled, nor do they forget to assemble parts, while also giving developers the ability to customize.

3. Loading of So

The successful loading of So is critical to the operation of Weex. After all, Weex requires a V8 engine to implement Js interaction with Native. It can also be seen in the source code that Weex modules will not be executed if So is not successfully loaded.

In online Bug collection, we will encounter UnsatisfiedLinkError errors. Although they are not frequent bugs, Weex cannot run once they occur. Weex SDK optimized the So loading section. Let’s look at the code logic of So loading:

public static boolean initSo(String libName, int version, IWXUserTrackAdapter utAdapter) {
    String cpuType = _cpuType();
    if (cpuType.equalsIgnoreCase(MIPS) ) {
      return false; } Boolean InitSuc = // MIPS is not supportedfalse;
    if(checkSoIsValid(libName, BuildConfig.ARMEABI_Size) ||checkSoIsValid(libName, Buildconfig.x86_size) {// check whether the size is normal /** * Load library with {@link System#loadLibrary(String)}
       */
      try {
        // If a library loader adapter exists, use this adapter to load library
        // instead of System.loadLibrary.
        if(mSoLoader ! = null) { mSoLoader.doLoadLibrary(libName); // Custom SoLoader to load by yourself}else{ System.loadLibrary(libName); } commit(utAdapter, null, null); InitSuc =true; } the catch (Exception | Error e2) {/ / So loading failureif (cpuType.contains(ARMEABI) || cpuType.contains(X86)) {
          commit(utAdapter, WXErrorCode.WX_ERR_LOAD_SO.getErrorCode(), WXErrorCode.WX_ERR_LOAD_SO.getErrorMsg() + ":" + e2.getMessage());
        }
        InitSuc = false;
      }

      try {
        if(! InitSuc) {//File extracted from apk already exists.if(isExist(libName, version)) { boolean res = _loadUnzipSo(libName, version, utAdapter); // Load So from the unzip packageif (res) {
              return res;
            } else{ //Delete the corrupt so library, and extract it again. removeSoIfExit(libName, version); // Unpack failed to load, delete; } } //Failfor loading file from libs, extract so library from so and load it.
          if (cpuType.equalsIgnoreCase(MIPS)) {
            return false;
          } else{ try { InitSuc = unZipSelectedFiles(libName, version, utAdapter); // Extract So from apk and load it; } catch (IOException e2) { e2.printStackTrace(); } } } } catch (Exception | Error e) { InitSuc =false; e.printStackTrace(); }}return InitSuc;
}
Copy the code

It can be seen that Weex has a number of guarantees to ensure the successful loading of So. The following flow chart is summarized:

Weex thread model

You old drivers know the benefits of multithreading also know that Android only main thread can update UI, for Weex it has its own complete set of working mechanism, if all tasks in the main thread that is bound to backlog too many tasks, resulting in tasks not timely execution at the same time there is a risk of stuck.

The Weex SDK also takes these into account. Analyzing the Weex mechanism, we can know that tasks are mainly spent in three aspects: JSBridge related, Dom related, and UI related. The three aspects are subdivided so that jsBridge-related operations are moved to the JSBridge thread and Dom related operations are performed in the Dom thread to avoid overloading the main thread. Here we can think of using asynchronous threads. Also, for monomial tasks such as Dom operations, it needs to be serial. If you use thread pools, you don’t really get the power of thread pools.

So that’s the analysis. Our requirements are clear: avoid resource consumption by asynchronous thread creation and destruction, and support serial execution. We can imagine that a threading capability that executes when there is a task and waits when there is no task would be perfect for our needs.

Fortunately, Android already provides such a class: HandlerThread. You can refer to my previous article, “Android Performance Optimization (11) : The Correct Asynchronous Posture.”

MJSThread = new WXThread("WeexJSBridgeThread", this); mJSHandler = mJSThread.getHandler(); MDomThread = new WXThread("WeeXDomThread", new WXDomHandler(this));
    mDomHandler = mDomThread.getHandler();
Copy the code

Weex thread model:

  • JSBridge is responsible for communication between JS and Native in WeexJSBridgeThread;
  • Switch specific Dom instructions to WeeXDomThread, responsible for various Dom operations such as parsing, Rebuild Dom Tree, Layout, etc.
  • Switch to the UI thread, responsible for native View creation, layout, event addition, data binding, etc.

Advantage:

  • Avoid main thread lag risk;
  • It avoids resource consumption such as thread creation and destruction.
  • Support serial operation;

5. Processing of parameter types of interactive functions

As for Weex RunTime, no matter how powerful it is, it is indispensable to interact with Native (method calls, using Native capabilities). The previous series of articles also analyzed the interaction principle of Module in detail. However, there is a detail problem that was not mentioned before, that is, the method signature of INTERACTION between JS and Native, can the parameter type only be String?

Back to WXBridge bridge of the communication, call Native methods will all come to callNative method, and then go to WxBridgeManager. CallNative method, finds that function in the body a line:

    JSONArray array = JSON.parseArray(tasks);
Copy the code

From this, it can be concluded that the arguments that JS passes to Native are not just plain String strings, but Json formats. In fact, you can find Json either at a breakpoint or through the WXStreamModule code.

  @JSMethod(uiThread = false)
  public void fetch(String optionsStr, final JSCallback callback, JSCallback progressCallback){
        JSONObject optionsObj = null;
        try {
          optionsObj = JSON.parseObject(optionsStr);
        }catch (JSONException e){
          WXLogUtils.e("", e); }... }Copy the code

However, these findings are not enough to solve our question: can arguments only be String? It must not be!

First, recall that one of the steps in the Module registration process is to get the annotated methods in the Module and store them in the mMethodMap; Where the method is actually called is the Invoke method of NativeInvokeHelper:

public Object invoke(final Object target,final Invoker invoker,JSONArray args) throws Exception { final Object[] params = prepareArguments(invoker.getParameterTypes(),args); // Parse the parametersif(invoker.isrunonuithread ()) {// Request execution on main thread is thrown to main thread execution; WXSDKManager.getInstance().postOnUiThread(newRunnable() {
        @Override
        public void run() { try { invoker.invoke(target, params); } catch (Exception e) {throw new RuntimeException(e); }}}, 0); }else {
      return invoker.invoke(target, params);
    }
    return null;
  }
Copy the code

Let’s trace the parsed parameter step in more detail:

  private Object[] prepareArguments(Type[] paramClazzs, JSONArray args) throws Exception {
    Object[] params = new Object[paramClazzs.length];
    Object value;
    Type paramClazz;
    for (int i = 0; i < paramClazzs.length; i++) {
      paramClazz = paramClazzs[i];
      if(i>=args.size()){
        if(! paramClazz.getClass().isPrimitive()) { params[i] = null;continue;
        }else {
          throw new Exception("[prepareArguments] method argument list not match."); } } value = args.get(i); // JSONObject and JSCallback types are handled separatelyif (paramClazz == JSONObject.class) {
        params[i] = value;
      } else if(JSCallback.class == paramClazz){
        if(value instanceof String){
          params[i] = new SimpleJSCallback(mInstanceId,(String)value);
        }else{
          throw new Exception("Parameter type not match."); }}else{/ / the other types of parameters params. [I] = WXReflectionUtils parseArgument (paramClazz, value); }}return params;
  }
Copy the code

Take a look at the parsing of other parameter types:

  public static Object parseArgument(Type paramClazz, Object value) {
    if (paramClazz == String.class) {
      return value instanceof String ? value : JSON.toJSONString(value);
    } else if (paramClazz == int.class) {
      return value.getClass().isAssignableFrom(int.class) ? value : WXUtils.getInt(value);
    } else if (paramClazz == long.class) {
      return value.getClass().isAssignableFrom(long.class) ? value : WXUtils.getLong(value);
    } else if (paramClazz == double.class) {
      return value.getClass().isAssignableFrom(double.class) ? value : WXUtils.getDouble(value);
    } else if (paramClazz == float.class) {
      return value.getClass().isAssignableFrom(float.class) ? value : WXUtils.getFloat(value);
    } else {
      returnJSON.parseObject(value instanceof String ? (String) value : JSON.toJSONString(value), paramClazz); }}Copy the code

Tracking down here is obvious: JS and Native interaction parameters support more than just strings.

Let’s summarize how Weex implements the interaction of different method signatures:

  • Module registration phase save Method;
  • The raw arguments passed by the Module method are in Json format;
  • The actual reflection calls the Method by getting the exact type of the parameter from Method, reading the corresponding value from Json, and converting it.

6, afterword.

This article mainly recorded my Weex source code reading process feel good can learn from the details, limited to the length of the article can not cover everything. In fact, not only Weex overall idea, Weex SDK code is also very good, it is very recommended that you read carefully, learning excellent source code for their own coding ability will have a certain degree of improvement!

Welcome to the Weex source code analysis project:Weex-Analysis-Project

Welcome to pay attention to wechat public number: regularly share Java, Android dry goods!