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 thecallCreateBody
CallAddElement 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 in
WXSDKInstance
Otherwise, 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 here
bindData
Method, 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, fromsetSrc
Start:
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.