preface

Componentization is a necessary path for any APP with complex business scenarios and products after multiple iterations. Componentization refers to the process of separating and recombining multiple functional modules when decoupling complex systems. Componentization is not only about module decoupling, but also a lot of work behind it to support componentization, such as module splitting strategy combined with business characteristics, interaction mode between modules and system construction.

This paper mainly describes how iQiyi knowledge APP explores and practices a set of efficient mobile terminal componentization scheme combining with its own business characteristics.

01 Background and objectives

1.1 background

Iqiyi Knowledge currently has multiple business carriers, including the knowledge plug-in of iQiyi mobile APP, the knowledge plug-in of iQiyi iPad APP, the knowledge plug-in of iQiyi mobile APP and the independent mobile APP of IQiyi Knowledge. As the online time of each terminal is different and the service functions carried by each terminal are not completely consistent, multiple terminals and multiple sets of codes result in high maintenance costs. First, when the same or similar functions need to be upgraded iteratively, both development and testing need to be carried out synchronously at multiple ends, and the cost increases exponentially. Secondly, with the rapid development of business, business modules are increasing, the dependency between modules is becoming more and more fuzzy, code coupling degree and complexity are increasing; In addition, it will be very difficult to add more business ends on the basis of the existing labor costs. Therefore, in the long run, it is very detrimental to efficient iteration of the business. The following figure describes the business module architecture of each end of iQiyi knowledge before componentization.

From the figure above, we can also see that there are many common business modules between each terminal, and the underlying support modules of each business module are almost the same. Therefore, we propose a componentization scheme suitable for iQiyi mobile terminal based on the business characteristics of iQiyi knowledge.

1.2 the target

We define the goals of componentization as the following:

· Solve multi-terminal code maintenance problems

According to the business characteristics, components are divided horizontally and vertically, and the iterative requirements are accepted by components as a unit, and components are reused at each end.

· Resolve issues of cross-component invocation and inter-component routing

Services are divided more clearly, the decoupling between components is more thorough, and communication between components is more efficient. The original service modules are separated and integrated, and the business boundary between components is defined.

· Improve development efficiency and facilitate development and debugging

Components can be compiled and debugged separately, enabling module developers to focus more on the module business;

· Improve integration and test efficiency

You can use tools to quickly integrate and test components needed by each end project.

Based on the above objectives, we designed a component division strategy suitable for iQiyi knowledge business. The following figure shows the functional architecture diagram after componentization, which is horizontally divided into basic components, functional components and business components, and vertically subdivided into components at each level. In terms of granularity, components include not only functional SDKS, but also business UIs. The purpose is that business modules are independent, with clear boundaries, and easy to expand and maintain.

02 Overall technical architecture

The technical architecture of knowledge componentization based on functional architecture is shown in the figure below.

The lowest level is the basic component, including Baselib and componentService. We make the common low-level implementation of the network library, Pingback, database, logging, and tool classes into the basic component, shielding the difference between the system and each end, located at the lower level of the functional and business components. All functional and business components use the same set of base components to ensure uniformity of the common parts. The base components are stable and do not iterate frequently.

Up a layer is a functional components, such as bearing play the ability of historical record player and components, bearing ability of payment and marketing components, bearing multiterminal, customized share the ability to share & poster components each side some basic functions, such as functional components between the fundamental component and the business component, functional components can according to the needs of the business component and iterative upgrade continuously.

Followed by the business component, the layer is each side may contain may not contain the core business module, in order to develop and maintain convenience, we will extract core business module for business components, such as search, screening, found that the feed flow, review and evaluation, job works, such as business components in the basic components and functional components of the upper, the iteration is frequent, But the business itself is more independent and has clear boundaries.

At the top is shell engineering, each end needs a main project to integrate the required components, collectively known as shell engineering, shell engineering includes the basic framework of each end, such as component registration and initialization logic, platform correlation processing logic, etc., as well as the unique business modules of each end, which are not suitable for extraction and disassembly.

On the right are the MoudleRouter and UIRouter that manage interactions and jumps between components. This part of the infrastructure is common, and all ends have to be integrated.

On the left is the build system, which is not in the componentized code but is a secondary system responsible for building components and application packages on each side.

03 Realization of core technology

The two core techniques in componentization practice are intercomponent interaction and intercomponent routing.

3.1 Component Interaction

The difficulty of component interaction is to reduce component coupling, preferably to achieve a completely non-intrusive invocation. The ModuleManager is defined as the lowest level of service components. Each component needs to provide the service interface to be invoked externally. The interface definition exists in the ModuleManager component. The ModuleManager code is non-intrusive to other component code and only parses the passed data and passes the call message to the corresponding component.

In order to solve the problem of URL hard coding and ambiguous dictionary parameter types, iOS uses Protocol in the componentization solution and registers its Class with the ModuleManager when the program starts running. Protocol is reflected as a string as a key, Class complies with the Protocol and implements the method defined by the Protocol. The external world obtains the Class through Protocol and instantiates it as an object, and calls the Protocol method implemented by the server. The timing of service registration of independent APP is different from that of each plug-in. Independent APP is when the program is started, while plug-in is when the external plug-in is called, and resources need to be unregistered when the plug-in exits. Protocol The Protocol is described as follows:

On the Android side, the ZRouter component is used for interaction between components. The implementation idea is similar to that on the iOS side, which refers to the SPI mechanism in Java (service discovery mechanism). Each component provides a service interface service externally, and the implementation of the interface is assigned to the corresponding component. During component initial registration, both the Service interface and the corresponding Service implementation are registered. When used by the business side, component functions need only be invoked through the Service interface. In this way, there is no direct dependency between components, realizing decoupling and isolation between components. The specific call is shown in the figure below:

3.2 Component Routing

In terms of the hop between components, iOS terminal adopts the way of REGISTERING URL, and the timing of registration can be divided into static, dynamic and lazy loading. Lazy loading means to check whether URL and ClassName have been registered and bound when calling the hop method. If not, it will obtain from the module static information table and complete the registration and binding. Handler can be specified during dynamic registration, so that the jump logic can be fully customized without the underlying unified jump logic. Also note that the plug-in side needs to release resources and cancel registration when exiting the plug-in.

For the realization of UI jump between components on The Android end, although the ZRouter mentioned above can also jump between activities, fragments and Views, the code implementation is too complicated. So we borrowed some of the best ideas in componentization and developed a UIRouter for UI jump between components. During compilation, the @RouterPath annotation added to the Activity generates a routing table with a Scheme Key or page short and a Value for the Activity. Jumping any Activity is handed over to the routing framework, which determines which Activity to start based on the routing table.

In order to improve the development efficiency and reduce the repetitive development of code during UIRouter initialization, we developed a Gradle plug-in that inserts automatic registration code and uses this plug-in to inject initialization code into specified methods through ASM at compile time.

This technique is also useful when registering component libraries; Component initialization classes are added to memory by reflection in Debug mode, and registration code is inserted by ASM in Release mode. This can shorten compilation time and improve development efficiency in debug mode, and reduce reflection consumption when running in the release official package.

The whole optimization process is as follows:

04 Building the System

With a clear hierarchy of components, how to quickly build components and projects becomes a must. In view of componentization, iQiyi knowledge team developed a set of rapid construction subsystem suitable for componentization combining with the company’s existing construction system.

Use a set of code in order to solve the crafty and the size of package in every plug-in end limits under the premise of the differences in the component library code control through macro segmentation, realize the difference code isolation, one end of the code only compile the current specified at compile time, when packaged by specifying the packaging to set up the macro configuration parameters, specified end complete build.

On the iOS side, each component is a separate project managed by a different Git private repository. Each component is integrated in the main project through CocoaPods, and all components are integrated into the main project as a two-party library. Although iQiyi Knowledge APP and each end plug-in adopt Cocoapods integration scheme, there are some differences in version dependence. In order to improve development efficiency, knowledge APP, as an independent application, directly uses the method of assigning git repository tag number to rely on component library. Plug-ins need to integrate the component libraries with their Podsec setting dependencies, which requires uploading the component libraries into binary files to the cloud and uploading the podSpecs to the private library. IOS plug-in side in the main project integration components are mainly divided into two ways into source code and framework, in the development and debugging phase of the source code, you can directly modify the code to complete the development requirements, in packaging and testing and release of the generation of framework, you can speed up the compilation speed will not be exposed to the source code.

Jenkins is chosen as the iOS terminal building systems, in initial stage of componentization, our building components is the basis of a first by building components, and then build the upper component to complete the whole building, as the component library, dependencies manually one by one after the trigger to complex building become the pain points of the build process, then start to build optimization, The introduction of Jenkins’ ParameterizedTrigger plug-in and its use in combination with shell scripts enable us to support the single construction of components, trigger multiple components when the main project is built, and configure dependent construction projects when the components are built separately. Implement a trigger to complete the building of all components.

When the component library is built, the code of the current iteration branch is checked for updates. If there are updates, the component library will be built. If there are no updates, the build will be skipped. With the increase of terminal construction, the system supports multi-terminal construction. The iPhone plug-in and iPad plug-in are different plug-in jobs, and the construction is completed through the differentiation of terminal scripts. The build system also implements build increments and timed builds, updating the version number in the PodSpec file after each build, and adding one to the previous build if the build is not specified manually during the next build. The build system connects to the enterprise’s internal instant messaging tool and sends notifications to subscribers when the build is complete. The following diagram describes the component build process:

On the Android side, each business component is also a complete individual and can be run as an independent App. It needs to meet the requirements of running and testing independently, which can improve compilation speed and development efficiency.

The current industry practice is that each component is a project, and a constant control in build.properties distinguishes different scenarios, and in Build. gradle, sourceSets sets the configuration for debug components separately, which is different from the independent runtime configuration for publish aar components.

But this solution for the client iQIYI knowledge is not applicable, because we componentized one of the main purpose is to achieve multiterminal components reuse, so also is the need for multiterminal adaptation situation, each of the basic configuration information, such as basic UI style is different, can’t be in each new component engineering configuration again. Therefore, directly use the shell Project of the original chaotic Project as the component debugging Project, set the runalone folder in the respective shell Project, control the compilation mode in the root directory build.properties through the constant isModuleType, and dynamically load the component dependencies required by the test. This allows you to test components individually in each environment.

At this time, the component-only debugging mode is actually equivalent to the component-shell engineering mode in the ideal state: only a small amount of configuration-related code, no other component-irrelevant page logic, and dynamic loading of components on demand.

The shell project root directory gradle.properties contains various constants, including side control, component library version number, compile environment control, runtime dependency control and run mode control parameters, etc.

IsDependenceMaven is used to control whether the dependency mode is source code or remote Maven. During development, the debug mode can be used for easy debugging. The formal environment uses remote dependency mode to save compilation time and easy reuse.

Maven_version Is used to centrally control component versions. When a version is upgraded, the corresponding component library version and dependent component library version are compiled, uploaded and updated in batches using the customized maven_publish upload script.

For Android SDK version and third-party library version, we separate them into a separate dependencies. Gradle.

Summary and Prospect

Iqiyi knowledge mobile terminal has basically achieved all the goals of componentization, business boundaries between components have become very clear, so that a component upgrade multi-end benefits, greatly improving the efficiency of development and testing. In addition, the flexible increase and decrease of components makes the cost of adding a new business bearer very low. You only need to combine existing components and modify the components for this new terminal, which enables iQiyi knowledge mobile terminal to support more terminals with less manpower. Some problems encountered in the process of componentization have been solved. At present, the componentization from the basic components at the bottom to the business components at the top have all been online, and the componentization construction system has also been put into production environment.

Of course, componentization is not achieved overnight, but a continuous process, which needs to be continuously optimized and improved in the future, so that componentization can play a greater role in the development of knowledge business.

I Qiyi Technology Sauce[Exposure] Professional players of god operation, fast step is not a dream. Double – finger double – click any position on the screen, spectacle gives you more wonderful! # Technology Enables entertainment # Spectacle # Amway # Unpack # Black Tech @iQiyi Video number

Maybe you’d like to see more

Iqiyi page dynamic component program evolution

Product workflow optimization: build component library to achieve high ROI