MCube dynamics and native engineering combined with best practices | JD Cloud technical team

The importance of cross-terminal dynamic development solutions has become increasingly prominent. This article summarizes the dynamic practice of our team's MCube to provide experience and reference for everyone.

access background

As the needs of our project iterated, pain points such as large business demand and high cost of separate development and release updates were exposed. Using H5 pages instead had differences in user experience and performance compared with native ones, so our team began to Research on dynamic transformation.

After making a series of attempts at dynamic development and investigating various solutions, we chose MCube's dynamic solution. The reason for choosing MCube is because it has the following advantages:

1. The solution is mature, and the access cost is very low for APPs that are also under the JD system. According to the documentation, the entire access process is not complicated. 2.MCube has rich atomic components, and the flexibility of the components is very high.

3. A complete component market can well meet our needs.

4. A set of XML templates is suitable for multiple platforms and can be reused.

We encountered compatibility issues during the access process. During the communication process, the students and developers in charge of MCube were very enthusiastic. The customized development was also very efficient. They were serious and responsible and answered all questions. The entire communication process was very joy.

Our team has now completed the complete process from initial attempts to dynamic transformation of an entire business page. During the period, we encountered many problems and summarized the best practice experience. This article summarizes it to provide experience for other students who have already connected or are preparing to connect.

Dynamic renovation of single floor

Although MCube's SDK provides a wealth of interface methods, for ease of use and subsequent full-page level dynamic control, we still do a lot of encapsulation and localization development.

First, at the data level:

According to the required parameter requirements of MCube SDK, we encapsulate the necessary module (page name) and templateID (template ID); in the data part, the model encapsulates the data source data used for the template, so that the dynamic view (DYNContainerView ) Get data from the data source for display, and it also facilitates external distribution; at the same time, we have a large number of dynamic transformation scenarios for native floors, and we need to retain the original data for interaction and downgrade, so the model reserves nativeData for native floors, which is convenient Use it when the above requirements are met; finally, considering that the page has a scenario where certain floors are displayed in groups according to sections, the model is designed with a group ID for group division. The same group ID is a section, according to the specific business needs of the page. Decide how the group will be displayed (cards or splits).

At the view level, based on the encapsulated DynamicViewModel, operations such as dynamic view loading are encapsulated to form a common floor CommonTableViewCell:

Common floor capabilities include:

1. Parse module and templateID

2. Call MCube SDK to download the corresponding dynamic template and give a callback at the end

3. Display the dynamic view DynView and refresh it with data

4. Supports setting the downgrade view and downgrading in case of failure.

5. For elements with native views, the general floor will use layoutId to find them and call them out through Block to facilitate subsequent operations, including width and height adjustment, proxy settings, etc.

self.successBlock = ^(DYNContainerView * _Nonnull dynamicView) {
    UIView *nativeView = [dynamicView getRrenderViewWithLayoutId:@"100"];
    if ([nativeView isKindOfClass:[原生视图类名 class]]) {
       //原生事件处理   
    }
};

Full page dynamic transformation

With the universal floors in place, we started the next step of transformation. The ultimate goal is to achieve dynamic delivery of the entire page. This is also our team’s new understanding of dynamic transformation: all floors of the full page support dynamic delivery. Native floors and dynamic floors coexist and are driven by data to flexibly control the display form of the floors. In addition, switches can be set by the configuration platform to control the degradation of dynamic floors.

Ideally, the order of business pages and floor data can be determined by the data source delivered by the backend. But when we actually carry out the transformation, we often face the situation that the result issued by the backend is mixed with the data fields of all floors. When loading the data model, each floor has to find the results it needs to use separately. Fields are parsed, so there are two disadvantages:

1. The floor sequence cannot be controlled by issuing

2. Unable to achieve floor-by-floor dynamic control

So the first step in our transformation is to redefine the distributed data structure.

"result":{
    "dynamicFloors":
        [
            {
                "type":"楼层类型",
                "module":"页面名",
                "templateID":"模版ID",
                "groupID":"part1"
                "data":{
                }
            }
        ]
}

We set up a dynamic floor array dynamicFloors, each element in it represents the data of a certain floor. type is used to determine the floor type. For dynamic floors, additional module and templateID will be issued to distinguish dynamic and native floors. If it is found that module and templateID are issued, DynamicViewModel is automatically used to parse, the data is set into the data source, and then CommonTableViewCell is used to load and display the floor. The flow chart is as follows:

In the future, the server can be pushed to deliver according to this structure. Of course, the front-end can also modify the delivered results first before pushing other teams to make changes. However, data security must be ensured and fault tolerance processing and downgrading switches must be done. .

Transformation results:



Trampling Q&A

Installation/compile error after accessing JDBDynamicModule

During the access process, we encountered problems such as class name and method name conflicts. We need to communicate with the relevant person in charge of MCube to carry out customized development of the SDK for the project alone and eliminate dependencies.

Which floors are suitable to be made into dynamic floors?

From our experience, a suitable dynamic floor must meet one or more of the following conditions:

1. There is a need for flexible changes in the floor display style.

2. The interaction form of the floor is simple and single. Dynamicization focuses on UI display, and the development of template interaction events is relatively cumbersome.

3. There is not much difference between Android and iOS. The difference here mainly refers to the difference in template development. Due to the mechanism of MCube itself, there are often situations where Android and iOS template fields are not common during template development. For details, see the "Differences in Android and iOS Template Development" section below. . Small differences can ensure that Android and iOS can use the same set of templates, reducing development workload.

Failed to load template via loadWithCallback

First check whether the module and templateID of the DYNContainerConfig object are set correctly.

Secondly, check whether the network loading bridge class and the <DYNNetworkProtocol> protocol are implemented. All network requests of MCube must be processed through the bridge class, making our local network requests and returning the results to MCube. First, create a class according to your own naming needs, and implement the <DYNNetworkProtocol> protocol. Register the created class in +load. This is mainly to tell MCube that all subsequent network requests will be handled by my class.

+ (void)load {
   [DYNMapperManager registeProtocolName:DYNProtocolConfig.networkProtocolName handlerClassName:NSStringFromClass(self)];
}

The main work in the bridge file is to implement the relevant methods defined in the protocol. The main things that need to be implemented in the DYNNetworkProtocol of network requests are:

getServerURL

According to your own network environment configuration, there are two sets of URL headers returned by the MCube interface request:

1. Formal environment: api.m.jd.com/XXX?functionId=

2. Test environment: beta-api.m.jd.com/XXX?functionId=

requestWithSetupModel: finish: cancel:

In this method, you need to create an API request, put the functionId, params, and appType in the setupModel into the input parameters, initiate the request, and return the request result to MCube for processing through the finish/cancel Block callback. We can also breakpoint here or perform our own processing. The main interface is to obtain the template list. You can check whether the issued template list file is normal.

getNetworkType

Returns the network type according to the current network environment, such as Wi-Fi, 5G and 4G, etc.

The ImageView picture in the template cannot be loaded

Requests related to image loading also require the creation of corresponding bridge classes to implement the relevant methods of the <DYNImageViewProtocol> protocol. At the same time, you also need to create a category UIImageView+DYNBImageViewHandler for UIImageView, overwrite the dyn_setImageWithURL: placeholderImage: and dyn_setImageWithURL: placeholderImage: completed: methods in the category, and use other tools such as sdWebImage to download images.

How to use native view components in templates

In MCube, you can write native view components as elements into templates. There are the following steps:

1. The native view component implements two methods in the <DYNCustomViewProtocol> protocol:

#pragma mark - DYNCustomViewProtocol
- (id)getCustomView {
    return self;
}

- (void)setDataWithValue:(id)value object:(id)object sender:(DYNViewLayout *)sender {
    if ([value isKindOfClass:[原生模型 class]]) {
        //使用模型刷新原生视图组件
    }
}

2. Declare the corresponding relationship between template elements and native view classes in +load:

+ (void)load {
    [DYNMapperManager registeProtocolName:@"模版元素名" module:@"模块名" handlerClassName:@"原生视图组件类名"];
}


3. Use the corresponding element name to write in the template

<FlexboxLayout layoutId="999" width="match_parent" height="wrap_content" flexDirection="column" justifyContent="flex_start" bgColor="#FFFFFF">
    <模版元素名 layoutId="100" width="100%" height="100%" data="${原生模型对应key}" bgColor="#FFFFFF">
    </模版元素名>
</FlexboxLayout>

Differences between Android and iOS template development

As mentioned above: Due to the mechanism of MCube itself, when developing templates, there are often situations where Android and iOS template fields are not common, for example:





During our development process, we encountered incompatibility issues between Android and iOS:

1. After Android's nested sliding component is connected to the project, it will report a conflict with the local library.

2. FlexboxLayout is set to 100% and can slide on Android, but not on iOS.

3.ImageView scaleType="stretch" Android will have a problem with the display ratio. You need to add useAspectRatio. iOS has no problem.



4. The syntax does not take effect when setting the gradient color on Android:



How to do complex logic in templates

1. Numerical settlement

<ImageView height="$calc(44*SCREEN_WIDTH/375)" width="$calc(SCREEN_WIDTH)" />

2. Conditional judgment

<ImageView src="@{${data.img1}?${data.img1}:${data.img2}}"/>
<View visibility="@{@{${data.show}!=null}?1:2}"/>

3. Gradient color (splicing syntax)

<FlexboxLayout bgColorList="$joint(${startColored},$unescape(comma),${endColored})">
<TextView text="$joint((${data.text1}  ${data.text2}))"/>

Upload and publish templates

The template must be uploaded to the configuration platform (please consult the relevant person in charge for the specific URL and enable permissions). Create the APP, module (page) and specific floor on the configuration platform. The floor can publish different versions, upload thumbnails and template files, and finally you can Choose whitelist publishing or grayscale publishing.



How to make a downgrade plan?

We have set up three sets of plans for downgrade in different dimensions:

view level

If MCube's SDK fails to load the dynamic view, it will give us a failure callback Block. Then our solution is to use the nativeData stored in the DynamicViewModel to load the native floor as a downgrade.

Deliver data level

You can control whether the floor is displayed natively or dynamically by controlling whether to issue module and templateID.

business floor level

We have set up a whitelist (dynamicWhiteList) on the configuration platform for control: locate specific floors based on module name (module) - template name (templateID), and configure true (downgrade)/false (dynamic) to determine whether to downgrade.

Data Format:

"dynamicWhiteList":{
    "页面名module":{
        "模版templateID 1":true,//降级
        "模版templateID 2":false//动态化
    }
}



Author: JD Retail Jiang Hai

Source: JD Cloud Developer Community Please indicate the source when reprinting

IntelliJ IDEA 2023.3 & JetBrains Family Bucket annual major version update new concept "defensive programming": make yourself a stable job GitHub.com runs more than 1,200 MySQL hosts, how to seamlessly upgrade to 8.0? Stephen Chow's Web3 team will launch an independent App next month. Will Firefox be eliminated? Visual Studio Code 1.85 released, floating window Yu Chengdong: Huawei will launch disruptive products next year and rewrite the history of the industry. The US CISA recommends abandoning C/C++ to eliminate memory security vulnerabilities. TIOBE December: C# is expected to become the programming language of the year. A paper written by Lei Jun 30 years ago : "Principle and Design of Computer Virus Determination Expert System"
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10320036