Android Engineering Modular Platform Design - Lecture Notes

This article is a text version of what I said in the 2018 [ Ctrip Technology Salon Mobile Technology Engineering ] technology sharing, and the redundant words in the speech were modified and deleted.
Published in [ CSDN Blog ], I hope it can help friends who can't buy tickets to participate in the conference.

Android Modular

Hello everyone, the theme shared with you today is "Design of Android Engineering Modular Platform"

Android Modular

First of all, let me introduce myself: My name is Zhang Tao, and I am currently working in the Ele.me mobile technology department. Maybe some of my friends know me. I also wrote some Android-related technical points on my blog [ Open Source Lab ]. If you have any questions about the modular design discussed today, or you can discuss in depth, you are welcome to add me. WeChat kymjs123chat.

Android Modular

The topic we are talking about today is based on project modularization. Everyone must know what modularization is. Let me ask you here, how many people have done modularization before, raise your hand and let me take a look ; Have you heard or heard of modularity? More this time.
We said that doing modularization is actually very similar to project refactoring. It is done from these points, but the focus is different. They are: delete, organize, downgrade, and decouple. So what do these four points mean, then let me share with you how I understand these four blocks:

Android Modular

Delete: Delete unnecessary files and reduce the project size as much as possible. Here is a set of data, I count the number of files before and after modularization of an APP of our Ele.me.
As you can see, .javathe number of files has decreased from 1677 to 1543. In fact, this is not the point. The point is the following. Only pictures and layouts are included drawablehere . After modular reconstruction, the number of files is reduced from 693 to 538. The number of image resources is reduced by nearly 200, and the size of the apk will also be reduced.drawablexml

Android Modular

Organizing means grouping code according to meaningful criteria. This is actually one javaof the purposes for which the package exists.
However, with the continuous iteration of the project, it is difficult to have time to group the classes according to the strict requirements. As you can see in the picture, our previous structure was very messy, because such a phenomenon was inevitable during the process of rapid project iteration and personnel replacement. So this is also a big thing to do when modularizing refactoring.

Android Modular

Next is what we often call cohesion and coupling, downgrading. We previously had a class called: Navigator, which is responsible for almost all Activitydirect jumps. That is, we will put all the startActivity()jumps into this class to write. It was fine when it was few before. As a result, when I saw this class, this class already had more than 200 methods, all of which were Activityjump methods, and some of them were repeated, that is, someone wrote a jump to A certain interface, the result came later, he did not know to write another one.

And what we do when doing modular refactoring is to first observe our own projects, which is a very important step in refactoring, which is to combine ourselves. Divide this class into three parts. We have two businesses that will jump frequently, but the pages to which the two businesses jump are in their own modules, namely the user module and the merchant module. Therefore, we set up two jumps in these two modules for the internal jumps of the module itself UserNavigator, ShopNavigatorand the jumps between modules or some small modules are used to Routerdo it. We have defined a routing library ourselves, in fact, to achieve Not much different from open source now.

Android Modular

Finally, decoupling is also the focus of today, how to elegantly remove the coupling between modules.
So far, we have been able to add or delete all modules that do not contain business state interfaces without changing a single line of code.
A specific example is this:

Android Modular

Alternatively, it could also be this:

Android Modular

The difference between these two pieces of code is that one is the state of manual management Debug, and the other is controlled by the compilation task handed over Gradle. The principle is the same.
And how to do this, the essence is: a module is a function, if you want your apk to have this function, just add this module and compile it together. This is what we mean by true componentization, zero coupling between modules, and zero changes to adding or removing modules.
For example, in the picture: debugthis module will definitely not be used in the official production environment; on the contrary, tinkerthis module will definitely not be used in the debugging phase. So I can not use the code related to this module during development.
Another example of use: I have an order module, and the order module needs to play a ringtone. For example, people often hear "You have a new Ele.me order, please handle it in time" in restaurants. But when I was developing the order module, if I had determined that there was no problem with ringtone playback, then I could choose a package that did not play ringtones during the development phase, and then added a package of ringtones until it was released online. When I don't add this ringtone module, I don't have the function of playing ringtones by default, but it does not affect the business functions of other order modules at all, and the addition or deletion of this ringtone module does not require any code modification.
Hearing this, I believe everyone is very curious about how this is achieved. Next, I will tell you about the internal principles.

Android Modular

All the core functionality comes from a library we wrote ourselves: IronBank. Take the [Iron Vault] from "From A Song of Ice and Fire", and call it the Iron Vault that cannot be owed.
The internal implementation of the Iron Vault actually uses the APT annotation processor to parse the annotations at compile time to generate a class, and let this class generate cross-module objects. Iron Vault uses a method similar to the back-end SOA design idea: inverting the active dependencies between modules into the provision and use of functions.
So what is the design idea of ​​SOA? We see a cartoon picture I drew: SOA is a service-oriented architecture model.

Android Modular

For example, on the left side of the picture, there is a service provider that provides media functions to the outside world. He told IronBankme to provide media services: "Hey, old iron, I have a media service here, and you can use mine when someone wants to use it."
On the other side, if there is a module saying yes at this moment, I need media services: "Old iron, do you have media services, I need to play a ringtone here!".
"Yes, to you."
IronBankThe media object provided to him by the service provider will be handed over to the service user.

Android Modular

Next, let's look at how the code is used: first, as a service consumer, which is the right half of the previous picture. We see that the traditional approach is to first declare an interface type, and then newassign a value to the implementation class of the interface.
When you use IronBankit, you don't need to care who the implementation class of the interface is. This is the IronBankonly use, to hide the implementation class, to achieve a thorough face-to-face interface programming.

Android Modular

As mentioned before, IronBankthe inversion of dependencies between modules changes from the previous service provider passively accepting calls from the caller to the service provider actively providing services to the caller.
So what do you need to do as a service provider? It's very simple. You just need to provide a public staticmethod to your object and add an @Creatorannotation to tell IronBankit that this is a creator method. You don't need to consider anything else. .

Android Modular

The IronBankapplicable scenarios mentioned above are stateless services, and when we develop business APPs, we are mostly objects with business states. For example, we usually wait until the user logs in before starting the long-chain and push functions. But specific to the code, the push module does not know when the user logs in at all, which is a business status issue.
In this regard, we introduced a BizLifecycle interface, which is actually similar to the Application object on Android. It's just that he manages the life cycle of the business, not the application.
So in terms of code logic, if each module cares about the business life cycle you need, it only needs to register a Lifecycle, and the registration process also only needs one annotation, which is solved by the compilation plug-in.

Android Modular

As you can see, in fact, such a capability can also be achieved with event notification, such as broadcasting or EventBus, but we deliberately blocked this method because it is difficult for you to track the function of event notification. Know where the recipient of a message is after it has been sent. I believe everyone can imagine how scary the project code will become if an application is flooded with broadcasts and events are received and events are sent everywhere.

Android Modular

At this point, all the capabilities of the entire modular decoupling have been introduced to you. Next, let's take a look at the structure of the entire project from a macro perspective. It is divided into three levels. The top layer is the business module, followed by some optional functional components, and the bottom layer is the public dependencies that are not related to the project.

Android Modular

In the end, the project structure is as shown in the picture. But if you do it directly, you'll be annoyed to death.
Why?
First: so many modules are compiled directly with source code dependencies, and the compilation time is at least more than 10 minutes;
second: the isolation of modules is almost 0, and anyone can still modify the code of any module, and it is very easy
; Three: After the release of the version, if there is a bug in a certain module, and then repair it, the concept of a version is lacking, especially when it is cross-team, there will eventually be a version split problem.

Android Modular

The solution I think everyone knows is to change the module reference to aar reference. The biggest advantage of aar reference is the management of module version and cross-team collaboration.
At present, the exploration of the Android field in China is getting deeper and deeper, and the application scale is getting larger and larger. In order to reduce the complexity and coupling of large-scale projects, and also to meet the needs of module reuse, parallel development and testing of multiple teams, etc., you must have a Set of suitable modular platforms.

Android Modular

Here is the modular platform we are currently using. You can feel it from this picture.
The main function of the modular platform is obvious, that is, it is used to build modules. On top of this, there is an implicit function, which is to concentrate the permissions of building modules, which can be more convenient for unified management; of
course, there are also the most important The advantage lies in the management of module versions. You can clearly know which version of the module the current main application is accessing, which is the latest SNAPSHOT build, and the update log of each version
; The communication on team collaboration is greatly reduced. If the module you have accessed or will be accessing is a module component developed by another team, you can directly follow it, all its version change logs and the latest version are all at a glance;
and The process of module testing and module release can be simplified through the platform. For example, when testing, if it is a release of a compatible version, you only need to tell the testing branch, and the testing can use the tag of the current online application and introduce the current The proposed module is recompiled to replace the old version of the module, and it is easy to control the variables.

Android Modular

After the introduction of platformization, let's look at it from the perspective of engineering structure: as far as we have tried, these two structures are the most suitable for Android engineering modularization. One is submodule and the other is multi-project.

Android Modular

First look at submodule: This structure is the default multi-module structure of Android, and there are multiple modules under a project. Each green square on the diagram represents a git repository, and then we see that all submodules are contained within the main project module. This structure is also the submodule structure supported by git by default. You only need to use the following git command to associate them together.
The advantage of it is that everything is default, and it is very intuitive for anyone to understand. Of course, he is also inappropriate, that is, during collaborative development, everyone app moduleis testing their own modules, it is easy to influence each other, and the git branch of the main project will be very complicated.

Android Modular

Correspondingly, multi-project can solve this problem very well: all modules are an independent project, they are in a parallel relationship on the file system, and the project where each module is located is a git repository.
However, this structure will have certain specification requirements for the project name, the main reason is when the module is jointly debugged.

Android Modular

We see that this code is written in a setting.gradlefile. It reads the local local.propertiesfile to get includethe source code of a module, so that the code of multiple modules can be easily modified when the modules are jointly debugged.
But he requires the folder name of each module project to be named after the module name plus this Project, for example, orderthe name of the project folder where the module is located. OrderProject
, I used the loop directly here. If you don't follow it, you may need to disassemble the loop and knock it by hand.
The above two engineering structures have their own advantages. There is no good or bad, only whether it is suitable or not. We also have teams in our internal two structures.

Android Modular

Then, here are the precautions for module joint debugging, that is, if your module is imported from source code, there may be other modules that reference the aar of the same module, which will cause conflicts. You need to judge by yourself, adding a custom method is also Well, you can also use the compilation plugin, which can make the source code reference and the aar reference mutually exclusive.

The main idea of ​​modular architecture is to divide and conquer. When splitting, the most important thing is to sort out the dependencies clearly, which are business modules and which are optional functional components. Finally, for the convenience of the team and faster adaptation, it is necessary to develop some auxiliary tools, such as IronBank, BizLifecycle, initialization scripts, etc. I mentioned earlier, which are all essential.

Android Modular

Finally, today's sharing is here, thank you all.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324690857&siteId=291194637