Essential skills for advanced engineers: Full analysis of Android component architecture! (With video + e-book + study notes sharing)

Preface

Nowadays, most apps will think of componentization or modularization when refactoring, especially in large factories or some large-scale companies, it is particularly important, the purpose is to facilitate App decoupling and optimization. In my understanding, componentization means to install each module with the same function one by one, and then use it in the form of library for our main app module to call, and no business logic will be carried out in the main app module, just packaging Well, in addition to the main app module, the other modules go back to their respective houses, find their own mothers, and do their own business logic. Simply put, componentization is to allow the library and application to switch between each other, and libray can be run as a separate App It can also be called to the main app as a dependent library. This construction idea can greatly improve development efficiency, reduce code maintenance costs, reduce code coupling, and make it easy for others to understand.

the whole frame

  • common: The basic component part, which has nothing to do with the business and requires all components to rely on together, such as: network request encapsulation, image loading encapsulation, ui-related base classes, tool sets, etc. (Of course, these contents can be placed on different foundations according to the layering principle module)
  • router-comp: route-driven component, carrying the routing work of the entire project
  • comp1: business component 1, such as video component, can run independently
  • comp2: business component 2, such as news component, can run independently
  • comp3: business component 3, such as video component, can run independently
  • app: Shell project, used to assemble each component into a completed app

Problems faced by componentization

  • Integration mode and component mode conversion (hot plug)
  • Page jump (routing) between components
  • Communicate between components and call each other's services
  • Packaging confusion

Componentized realization

In response to the problems mentioned above, we will explain their solutions one by one below. After solving these problems, you will find that you have built a component-based project. The following figure is a complete componentized project structure: common is the basic component module, which exists as a library and requires all components to depend on; comp1 and comp2 exist as components and can be configured as a library or a module that can run independently; app is a shell, through assembly Components realize their value.

Integration mode and component mode conversion (hot plug)

The Android project is built through gradle, and the different performance of the module is realized by configuring the gradle of each module. The module of Android Studio has two attributes, namely:

  • Application attribute: can run independently, that is, our app
  • library attribute: can not run independently, the library is dependent on the app

The module attribute is configured through the gradle file in its directory. When the module attribute is application, the module exists as a complete app and can be run independently to facilitate compilation and debugging; when the module attribute is library, the module is used as a dependent library. The shell project depends on and is assembled into an app. So how can these two modes be automatically converted? If you manually modify the configuration of each component each time you switch modes, it is acceptable if there are fewer components, and it will be very inconvenient if there are more components. Let’s talk about how to achieve automatic conversion between the two modes. . 1. First, declare global configuration variables to identify the properties of the module (app or library). For example, declare the boolean variable ext.isModule in the build.gradle file under the project directory. True represents the component as a standalone app, and false represents The component is a dependent library, as shown below

buildscript {
 ext.kotlin_version = '1.3.21'
 ext.isModule = true //true-每个组件都是单独的module,可独立运行 false-组件作为library存在
 repositories {
   google()
   jcenter()
 }
}

2. Configure the build.gradle file of the component

//1 if (rootProject.ext.isModule) {
//可独立运行的app
apply plugin: 'com.android.application'
} else{
//被依赖的library
apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 28
defaultConfig {

//applicationId "com.study.comp1" //2 如果没有,默认包名为applicationId
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
//3
sourceSets {
main {
if(rootProject.ext.isModule){
manifest.srcFile 'src/main/java/module/AndroidManifest.xml'
} else{
manifest.srcFile 'src/main/java/library/AndroidManifest.xml'
java {//移除module包下的代码
exclude 'module'
}
}
}
}
}

The above is part of the code of the intercepted component gradle, which contains all the content that needs to be configured for componentization, and each point is commented

  • Note 1: As mentioned above, according to the value of isModule, set the attributes of the module as app or library
  • Note 2: When the module attribute is library, applicationId cannot be set; when it is app, if applicationId is not set, the default package name is applicationId, so for convenience, applicationId is not set here
  • Note 3: Android Studio will generate the corresponding AndroidManifest.xml file for each module, declaring the required permissions, four major components, data, etc.; when the module attribute is app, its corresponding AndroidManifest.xml needs to have a complete app location All the configurations needed, especially the activities that declare Application and launch; when the module attribute is library, if each component declares its own Application and launch Activity, then conflicts will occur during the merge, and the compilation will not pass. Therefore, you need to redefine an AndroidManifest.xml file for the current module, do not declare the Activity of Application and launch, and then specify the path of AndroidManifest.xml according to the value of isModule, so there is the way of writing in Note 3. In order to avoid naming conflicts in integrated mode, it is a good way to name each file with its own module name prefix. The following figure is the directory of the module

Change the variable value declared in step 1 when you need to switch the module attribute, and then recompile

Page jump (routing) between components

In the componentized architecture, different components are balanced, and there is no interdependent relationship (refer to the architecture diagram at the beginning of the article). Therefore, suppose you want to jump to the page in component B in component A. If you use Intent to explicitly jump, it will not work. And everyone knows that Intent implicit jump management is very inconvenient, so Arouter appeared , And there is a strong technical team support, you can use it with confidence. So how to apply Arouter in a componentized architecture? Let's talk in detail below

1. Dependency processing

Depend on Arouter in the common component, and configure the compilation parameters; introduce the aouter compiler plug-in in the business component, and configure the compiler parameters at the same time. The following is a partial fragment of the gradle file of the Common component

//配置arouter编译器参数,每个组件都需配置 kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}

dependencies {
//arouter api,只需在common组件中引入一次
api('com.alibaba:arouter-api:1.4.1') {
exclude group: 'com.android.support'
}
//arouter编译器插件,每个组件都需引入
kapt 'com.alibaba:arouter-compiler:1.2.2'
}

2. Initialization and coding implementation

In the component architecture, we often encounter situations where the component needs to use the global context. When the component attribute is app, it can be implemented by customizing Application; when the component attribute is library, the application cannot be called because the component is dependent on the app. Instance, and there is no Application; therefore, the solution given here is to create a BaseApplication in the common component, and then let the Application in the integrated mode (component mode) inherit the BaseApplication, get the global Context in the BaseApplication, and do some initialization To work, Arouter needs to be initialized. The following is the BaseApplication declared in the common component.

abstract class BaseApplication : Application() {

companion object {
var _context: Application? = null
//获取全局Context
fun getContext(): Application {
return _context!!
}
}
override fun onCreate() {
super.onCreate()
_context = this
//初始化Arouter
initARouter()
//初始化其他第三方库
}
private fun initARouter() {
if (BuildConfig.DEBUG) {
ARouter.openDebug()
ARouter.openLog()
}
ARouter.init(this)
}
override fun onTerminate() {
super.onTerminate()
//清理Arouter注册表
ARouter.getInstance().destroy()
}
}

According to the routing characteristics of Arouter, after initialization, you can register the page with @Route annotation, and then call the Arouter api to realize the page jump (the so-called cross-component page jump here refers to the integrated mode, not the component mode) , It doesn’t matter whether they are under the same component, suppose we want to jump from the component 1 page with parameters to the component 2 page, please see the example below

/**
* 在组件2中通过@Route注解注册该页面
*/ @Route(path = "/comp2/msg",name = "我是组件2的MSGActivity")
class Comp2MsgActivity : BaseActivity() {
//传递过来的参数
@Autowired(name = "msg")
@JvmField
var msg: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//注入传递的参数
ARouter.getInstance().inject(this)
setContentView(R.layout.comp2_activity_msg)
comp2_msg_msg.text = msg!!
}
}

//在组件1中发起跳转命令
ARouter.getInstance()
.build("/comp2/msg")
.withString("msg", "hello Im from Comp1")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.navigation()

The above has completed a simple page jump across components, which is just the basic use of Arouter. After solving the problem of page jumps between components, let's take a look at the implementation of communication between components and calling each other's services. Communication between components, calling each other's services , communication functions and routing functions between components have something in common, that is, they are all realized by the basic functions of Arouter. The Arouter driver layer defines the interfaces provided by each component, and then implements the interface in the component's own module. , Call other component services through Arouter. Suppose we need to call the service in component 1 in component 2, which can be summarized as the following 3 points

  • Define interface: Define the interface CompServer1 provided by component 1 in the common component. Note that the interface type is Arouter template type IProvider
/**
* 组件1对外提供的接口
*/
interface CompServer1 : IProvider {
fun showMsg(msg: String)
}

  • Implement interface: implement the interface defined above in comm1 and register through @Route annotation
@Route(path = "/comp1/server",name = "comp1对外提供的服务")
class CompServer : CompServer1 {
var mContext: Context? = null
override fun showMsg(msg: String) {
Toast.makeText(mContext,msg,Toast.LENGTH_SHORT).show()
}
override fun init(context: Context?) {
this.mContext = context!!
}
}
  • Calling the service: After completing the definition and implementation of the component 1 interface, call the interface where needed in component 2
val server1 = ARouter.getInstance().build("/comp1/server").navigation() as CompServer1
server1.showMsg("我从comp2吊起了comp1的接口")

Does it feel simple? ? That's right, it's that simple, hurry up and use it! Haha

Packaging confusion

Speaking of obfuscation, some people may wonder, can it be mixed in various components? This confusion is not recommended! ! Because the component is built into a release type aar package by gradle in the integrated mode, if obfuscated in the component, once the code has a bug, it is difficult to track the cause of the bug according to the log at this time, and different components are confused separately It is very inconvenient to maintain and modify, which is why it is not recommended to configure buildType in business components. Therefore, the code obfuscation of the componentized project is placed in the app shell project in the integrated mode, and each business component is not configured for obfuscation. In the integrated mode, enable obfuscation in the release build mode of the app shell project .gradle file. The other buildType configuration is the same as that of a normal project. The obfuscated file is placed under the app shell project, and the code obfuscation of each component is placed in the obfuscated file.

At last

Above, we have solved the various problems faced by componentization one by one. So far, we have built a simple componentized architecture project. Is it not difficult to realize it without knowing it? Now, let me summarize the advantages of componentization.

  • Decoupling: 90% of the business component code is decoupled from the project, so it is 90% instead of 100%, because the business component needs to declare the open interface in the common component. Is there any way to achieve complete decoupling? What? No better way has been found yet. . .

  • Improve development efficiency: relying on the advantage of decoupling, team members can only focus on the components they are responsible for, and the development efficiency is higher; moreover, only compile their own modules during the component development process, which greatly shortens the compilation time and avoids long Waiting for compilation situation.

  • Clear structure: Under the premise of clear separation of business components, the project structure becomes extremely clear, which is very convenient for overall control.

In order to facilitate you to learn more about the principle of component architecture and the interpretation of the underlying source code, I have collected and compiled a set of component learning materials of video + e-book + study notes. The video is the "Learning Componentization from Scratch" and the componentized learning e-book "Android Componentized Architecture", and a set of "Third-Party Open Source Framework Analysis and Study Notes", including plug-in, taught by Iqiyi senior engineer Lance. Component analysis, hot fix and other popular third-party framework analysis study notes!

Friends who need the above componentized learning materials can click here to get it for free after liking + any comment !

Componentized learning video
Componentized e-book
Open source framework to interpret the catalog and part of the analysis content

Friends who need the above componentized learning materials can click here to get it for free after liking + any comment !

Guess you like

Origin blog.csdn.net/star_nwe/article/details/111173661