Gao Jiajun, a tmall wireless expert at last year’s GMTC conference, shared how tmall iOS decoups and mentioned the modular solution BeeHive, which he later organized into an article and recommended reading:
The road to mobile phone tmall decoupling
In this article, Dai Peng of Tmall continues to share the purpose of BeeHive, illustrate best practices with examples, and analyze its structure and principles.
1. Why BeeHive?
In the process of rapid development of Tmall App, the number of code increases with the increasing number of staff and business complexity, which brings various problems in collaborative development.
Have you ever struggled to develop in this environment? Be afraid to do requirements while fixing bugs.
And the coupling of iOS engineering code might look something like this:
The AppDelegate contains a large library of init and other operations, ranging from hundreds to thousands of lines of irrelevant code, which is expensive to maintain, and the call logic of different libraries is interlocked, as shown in the figure below:
As a result of the spaghetti coupling, the upper-layer business is limited by the dependence of the underlying base library, and the BUG detection is slow. The efficiency of adding new functions decreases with the increasing amount of code.
1.1 Main problems in development
During the development process, the following problems are summarized in App development:
-
The dependencies between functional codes are complex and maintainable
-
In the process of collaborative development, there are blocks in parallel development
-
If function boundaries are unclear and basic function modules change, upper-layer services are affected
-
Each team is responsible for the functional modules, and there is coupling code in the main project
-
Upper-layer services provide functions to lower-layer services in reverse order
-
Performance analysis optimization becomes difficult as code increases
1.2 Appeal of App and developers
An App should have the following features:
-
Functional maintainability
-
Functional availability
-
Function with good performance
-
The function can be analyzed and quantified
-
The functionality is unit testable
Developers want to do the following in collaborative development:
-
Don’t want to be block development
-
Interfaces that depend on library versions and conventions should be stable
-
Access a feature with minimal intrusive code
The code isolation development problem is solved through Cocoapods, and the code level is separated, but the coupling of logical functions is still not solved. Developers hope to achieve fast and stable business expansion at the same time, so there needs to be a kind of App module decoupling way to let developers from the pain of dependency, so let developers have the idea of building a BeeHive global foundation framework.
2. BeeHive best practices
For details about how to use BeeHive, see the README of BeeHive. Here’s an example from actual development.
2.1 3 d – Touch example
2.1.1 Scenario 1: Building a 3DTouch scene
Since 3d-touch is available on iPhone 6s and up, almost all apps have adapted to its features, including the following code in the AppDelegate as a convention:
-(void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
{
....
}Copy the code
This means that the AppDelegate grows the number of lines of code, but there is no need to write the QuickAction functionality in the AppDelegate. Using BeeHive framework features, create 3DTouch Pod, independent 3DTouch related business functions.
-(void)modQuickAction:(BHContext *)context
{
....
//process context.shortcutItem
}Copy the code
2.1.2 Scenario 2:3DTouch needs to be dynamic and quantifiable
When a dynamic configuration quickAction requirement comes along, the old practice is to introduce the configuration Module and create a corresponding series of calls. Then you just need to call the configuration Service and want to update the quickActionItem earlier, so you can call modInit to achieve this.
-(void)modQuickAction:(BHContext *)context
{
....
//update config by configCenter Service
}Copy the code
The product side also wants to know which quickactions the user is using, so it can call the UserTrack Service. For example, the developer needs to call the Log, Cache, and other services, using the BeeHive Service in the form of a one-line call.
2.1.3 Scenario 3:3DTouch needs to be personalized
In the absence of a server, how to personalize QuickAction, register and provide 3DTouchBHService, to other business calls such as a function page
-(void)updateAccessTimesWithActionURL:(NSURL *)actionURL
{
....
// save view controller access times by cache service
// update local quickAction Items by access times and any other element
}Copy the code
The above three typical scenarios mainly involve BeeHive functions:
-
Module creation, aware of App life cycle
-
Import and call Service internally
-
External Service
-
Function transplant, no copy, add pod source to podfile
The whole 3DTouch development process does not involve the specific implementation of other functions, slice-oriented programming process, as long as you care about the corresponding needs of your module.
3. BeeHive structure and principle analysis
BeeHive borrowed from the Spring Service, Apache DSO architecture concept, using AOP+ extension App life cycle API form, the business functions, basic functional modules in modular way to solve complex problems in large applications, and between modules in the form of Service call, complex problems will be segmtioned, Using AOP to modularize services, for example, logging and burying module using AOP, the business side does not need to consider logging and burying related code, just declare to call Service with createService.
The corresponding BeeHive architecture is as follows:
In the form of Core + plugin, the main process of an application can be centrally managed. Different modules exist in the form of plugin, which is convenient for horizontal expansion and transplantation.
BHContext, the BeeHive configuration file, provides global unified context information.
The BHCore in the figure is BeeHive, which provides registration, Module creation, and Service logic. The Module, Service registration, and call logic are only related to core modules. There is no direct association between modules.
The core idea of BeeHive involves two parts:
-
Calls between modules are changed from directly calling corresponding modules to calling services, avoiding direct dependencies.
-
The distribution of the App lifecycle logically splits the coupling in the AppDelegate, and each module exists independently as a microapplication.
BeeHive provides three different invocation forms, static plist, dynamic registration, and annotation. Module and Service are not associated. Each Service Module can independently implement functions of Module or Service.
3.1 the Module
(Click to enlarge image)
The diagram contains the main BeeHive startup process and the timing logic for the Module. Module event distribution is derived from triggerEvents in BHAppDelegate, and the corresponding GlobalContext is also provided to the business side in the callback.
In addition to calling back system events, the BHAppDelegate extends the App life cycle by adding ModuleSetup, ModuleInit, and ModuleSplash, and developers can also extend them themselves.
(Click to enlarge image)
In the process of extending the cycle, Module analysis and quantification function is added at the same time, so that the time of Init of each Module can be calculated, which provides data support for performance optimization. In the process of increasing services of an App, the Module to be optimized can be determined by analyzing the Init time of Module positioning.
After following the BHModuleProtocol, the Module can capture App status callbacks and has a global context during the App life cycle. You can obtain configuration parameters, Module resources, and service resources through the context.
For App with BeeHive as the underlying framework, in addition to the convenience brought by decoupling, developers can directly transplant modules with the same functions in the process of developing new apps without repeating the wheel. Developing an App is like assembling building blocks, which can combine the required functions and businesses.
3.2 the Service
(Click to enlarge image)
The above figure contains the logic related to Service. Business A can invoke the Service directly through createService, and Module can dynamically register A Service as required. The core of Service invocation and implementation is BHServiceManager. A separate Services Interface Pod can be created to place the Services to be used in a unified manner, so that the business dependency changes from a network to a central one, with the business side relying on only one of the Services.
Services can dynamically share objects and load them on demand. BeeHive logic registers basic services in PLIST. Business services allow services to be dynamically registered without first being registered until the Service needs them.
Service supports two different modes:
- Singleton: For globally unified and stateless services, you are advised to use this creation mode to facilitate unified Service management and reduce unnecessary memory consumption.
- Multi-instance: Each time a service is invoked, a new service is recreated. The multi-instance approach is best used for services that involve state and state changes.
Service read/write problems encountered in multithreaded environment. Lock was used to avoid Array crash.
However, Service has the following problems:
- A Service dependency that causes the underlying dependent Service to be called without being created.
- Plan the creation sequence of Service and Module to make the App open in seconds and optimize the performance experience.
The former dependency problem is planned to be solved by scheduling mechanism, while the latter needs to be stripped and practiced more by AppDelegate, which is not discussed here.
4. Thinking behind BeeHive
BeeHive solves the coupling problem in the collaborative development of multiple teams and developers by distributing App state and unifying Service Interface architecture. For the development cost in the process of practice, adaptation needs a certain process, but after the logic is straightened out, the application is not a problem. In terms of revenue, BeeHive is better suited for large, multiplayer and fast-moving projects than for small, complex projects.
At this point, BeeHive in the main has been analyzed in place, BeeHive is a growing iOS framework, the current Star has 1500+, I hope we can pool wisdom, more issue, Pull Request, so BeeHive can also let more people benefit. Imagine gracefully building each cell module like a bee.
Reference 5.
- Spring Related information
- Apache DSO reference link
- Cocoapods data
Thanks to Xu Chuan for correcting this article.
To contribute or translate InfoQ Chinese, please email [email protected]. You are also welcome to follow us on Sina Weibo (@InfoQ, @Ding Xiaoyun) and wechat (wechat id: InfoQChina).