preface

In actual development, Weex has built some basic components that can be quickly integrated into applications, but it certainly cannot meet the changing scenarios and functional requirements. Therefore, Weex provides a capability extension mechanism for customizing functions based on service requirements.

The Module extension

Module refers to a non-UI specific function. For example, sendHttp and openURL. Developers need to customize modules to inherit from WXModule and register them as required to use them.

The Module of registration

WXSDKEngine provides a number of overloaded ways to register a Module. The most common is the registration method that maps the Module name to the Class:

public static boolean registerModule(String moduleName, Class<? extends WXModule> moduleClass) throws WXException {
    return registerModule(moduleName, moduleClass,false);
}

public static <T extends WXModule> boolean registerModule(String moduleName, Class<T> moduleClass,boolean global) throws WXException {
    returnmoduleClass ! =null && registerModule(moduleName, new TypeModuleFactory<>(moduleClass), global);
}
Copy the code

The Module registry involves multiple classes, as shown in the sequence diagram below

Registration basically defines a mapping that allows one party at run time to find another party to call based on the identity. The registration here is divided into Native registration and Js registration

  • Native to register

    Wrap the custom Module Class as ModuleFactory, and then save the Module Name and ModuleFactory mapping in sModuleFactoryMap. If global, the Module instance is directly reflected and stored in the sGlobalModuleMap

  • Js registered

Map is used to store the set of method names marked with JSMethod annotations in The moduleName and custom Module. This reflects the getMethods used to get the methods in the Module, so the methods provided to be called need to be defined as public, and the qualified methods in the parent class can be called. The map data format is {‘moduleName’:[‘updateAttrs’,’updateStyle’]}. Weex will internally package its own data structure WXJSObject, convert it to byte array, and hand it to WXBridge for processing. The method type is METHOD_REGISTER_MODULES (registerModules). Remember WXBridge, which takes the heart of Native and Js interaction.

  • Native+Runtime

    JS registration is much more complex than Native. As mentioned above, map is converted into byte array for convenient transmission, and then it is completed by WXBridge calling Native method. The corresponding entry is in wx_bridge.cpp:

    auto result = WeexCoreManager::Instance() - >getPlatformBridge() - >core_side() - >ExecJS(instance_id.getChars(), name_space.getChars(),function.getChars(),params);
    Copy the code

    Weex_core_manager =>platform_bridge=>core_side_in_platform, and then weex_Runtime exeJS is executed. The Weex Runtime is weex-main-jsfm.js(the default Weex sandbox can also be set to main.js), which together with V8 and JNI forms the underlying engine of Weex. The JS file is packaged in assets and is first loaded by V8 when the SDK initializes.

The Module to invoke

For example, the front end calls the Stream component to request interface data:

var stream = weex.requireModule('stream') stream.fetch{... }Copy the code

This code is processed by js-framework, starting from native, through SystemMessageHandler#handleMessage->nativeRunwork, and then handed over to WXBridge. The sequence diagram is as follows:

  • Start with WXBridge and run in the WeexJsBridgeThread thread
  • From the sModuleFactoryMap mentioned earlier during registration, extract the Factory that builds the module based on the module name
  • Find the registered Module by findModule, which was created globally at the beginning. If not, return it directly if it already exists in the map, otherwise reflection creates and stores the map. Therefore, the same Module instance is invoked multiple times with the same instaceId
  • Use the method name to find the corresponding MethodInvoker in the Factory, which wraps the method, parameter type, and whether it runs on the main thread (the default is), and then hands it to NativeInvokeHelper. You do parameter parsing, thread switching, and finally call the target method via reflection

The callback JSCallback

In many cases, after JS calls Module, we need to return the result of some calls to each other. In this case, we need to use JSCallback.

  • As mentioned earlier, NativeInvokeHelper processes parameters. Here Weex uses FastJson, and the input parameters of the call method are represented by JSONArray. The JSONArray of FastJson implements the List interface, and the JSONObject implements the Map interface. Easy to expand.
  • When processing a parameter, if the parameter type is JSCallback, it is wrapped as its unique implementation class SimpleJSCallback
  • When called back, WXBridgeManager#callbackJavascript is switched to WeexJsBridgeThread by a Handler
  • Finally, the native method of WXBridge is used to call back JS

Component extensions

Component refers to a Native control that implements a particular function. For example: RichTextview, RefreshListview, etc. Developers need to customize components that inherit from WXComponent(there is also WXVContainer, but these also inherit from WXComponent) and register them as required. Component is also the most widely used of Weex’s extensions because interface rendering is built around Component.

Component of the registration

Similar to Module registration, there are Native registration and JS registration. The difference is that Native registration stores the mapping between the component name and IFComponentHolder, similar to the global parameter of Module. Holder’s loadIfNonLazy method will fetch all annotations of the registered component. If it contains @Component and its lazyLoad==false(default: true), parse and save its methods to holder immediately; otherwise, only parse and save on first use. Note that Component provides two types of front-end calls: properties and methods

Component of the call

attribute

Componentprop (Name =value(value is attr or style of DSL))), and the method must be public. This is also the most commonly called method in the front end, which is often used to initialize the control to display the necessary values, such as TextView text, ImageView SRC, etc. This process is often accompanied by the page rendering process, more complex than the method call, here is the most commonly used image control WXImage as an example:

@WXComponentProp(name = "src")
public void setSrc(String src){... }Copy the code

SystemMessageHandler#handleMessage -> nativeRunwork -> WXBridge#callCreateBody Considering thecallCreateBodyCallAddElement creates the root node and adds child elements.

  • There are two key steps in initializing a component before assigning a value to a property :callAddElement and callLayout
  • Weex calls to UI controls are wrapped up in actions. CallAddElement creates GraphicActionAddElement. If the layout is to be updated next, it is stored inWXSDKInstanceOtherwise, switch directly to the main thread to execute the Action
  • CallLayout pulls the GraphicActionAddElement from the Map and switches to the main thread via WXRenderHandler to perform the task. WXComponent will be called herebindDataMethod, finally get the key-value set of setting attribute value, and call the corresponding custom setting attribute method
  • Connecting the two key steps is Weex’s message loop mechanism, similar to Android’s Handler, which is put in one place and taken in another. Weex will sink this to the bottom, the specific implementation of the Source code in weex_core/Source/base/message_loop

Note that creating GraphicActionAddElement in callAddElement is a simple line of code, but that’s all the work that goes into creating the custom Component instance

methods

The method is declared with the @jsmethod annotation as in Module, and the call starts with WXBridge#callNativeComponent, as in Module. Methods are used less in controls than properties, because control methods are triggered more by user actions than direct front-end control.

Adapter extension

Adapter is not a concrete base class, but a set of interfaces. Weex provides unified interfaces for some basic functions. Developers can customize their own services by implementing these interfaces. You can think of it as an adapter that provides basic capabilities for upper-layer Components and modules.

Adapter registration

The registration of Adpter is relatively simple, passing in its implementation class through InitConfig’s Builder mode and saving the reference to WXSDKManager for Component and Module calls.

Adapter calls

Is actually calls to the method in a real implementation class, such as the above WXImage, when set the SRC attribute to a common Adapter: IWXImgLoaderAdapter

private void setRemoteSrc(Uri rewrited, int blurRadius) {... IWXImgLoaderAdapter imgLoaderAdapter = getInstance().getImgLoaderAdapter();if(imgLoaderAdapter ! =null) { imgLoaderAdapter.setImage(rewritedStr, getHostView(),getImageQuality(), imageStrategy); }}Copy the code

IWXImgLoaderAdapter is responsible for Weex image loading, because image loading is very important and there are many third-party class libraries, so through this way Weex framework and image library decoupling, and does not provide a default implementation. It’s up to the caller to decide whether it’s Picasso, Glide, ImageLoader or something else. The sequence diagram is then called from the WXImage property above, fromsetSrcStart:

In addition to images, Weex also provides many interfaces, including network, log, storage, exception reporting, and logging.

conclusion

Weex uses the extension capabilities of Module, Component, and Adapter to build a basic framework for integrated applications. On this basis, developers can enrich their interfaces and improve their service logic in accordance with common standards.