组件化和模块化的区别
- 组件化的重心主要是放在业务逻辑层,主要是为了拆分业务逻辑,只针对业务逻辑
- 模块化的重心主要是为了功能的重用,那功能拆分一个个的插件,针对整个项目
组件化和插件化的区别
- 组件化我们不管把我们的业务拆分为多少个模块,最终在打包上线的时候我们都会生成一个apk
- 而插件化也是拆分为很多插件模块,但是在最终打包之后,成为了很多apk,最终我们把它上传到我们的服务器上面,用户使用的时候,只需要下载响应的apk即可,然后使用动态加载技术,加载里面相应的Activity
组件化开发的优势
- 相当于我们每次运行不需要整个项目运行,而是运行单一的组件即可
- 如果我们要将某一个模块用到新项目中去,就很简单了,因为我们每一个模块都是一个独立的Application
- 因为Application是不能依赖其他的Application的
- 我们就不需要解耦合了、资源等等
- 大团队开发中,组件化开发是开发的基石
业务逻辑层
公共层
系统层
组件化开发要遇到的问题
从组件化实战来解决问题
- 创建组件化项目
- 创建两个Application组件,注意修改MainActivity的类名,以防止资源命名冲突问题
- 我们增加了这么多模块,那么每个模块里面都有build.gradle,假设后面我们需要升级某一个模块中依赖的组件的版本,我们需要修改每一个模块中的版本号么?这样肯定是不行的,所以我们需要创建一个文件,统一管理我们的版本号,而在各个模块中使用相同的版本控制工具才行
- 这里有三种方式
- 第一种方式:直接定义到gradle.properties,简单粗暴
- 第二种方式:定义到我们整个项目的build中
- 第三种方式:我们自己创建一个Gradle文件,专门去装我们这些参数
- 我们采用第三种方式
- 文件创建好了,但是我们的项目是不知道的,怎么让整个项目都能使用呢?
- 在项目的build.gradle中声明即可
- 这样声明了就是告诉项目在我们程序加载的时候去加载我们的config
- 然后我们就可以在主App和子Module下面使用我们的config文件了
- 改完之后,Sync now一下即可,没有报错就是成功了
- 新的问题来了,我们通常修改完SDK版本号之后,我们下面的各种依赖库的版本也要与之对应,怎么办呢?
- 当然,我们也可以通过同样的方式,定义一个数组来解决问题
- 同样的方式,在各个组件的依赖下面引入即可
- 写到这里,会有疑问,gradle文件里面,为什么有的直接用大括号?有的用等于号,有的用冒号,这里解释下,直接用大括号表示数据集的意思,后面等于号表示一个对象的意思,冒号则是某个属性啦
- 写到这里,我们就将各种依赖库的版本号给统一管理啦
- 下一个问题
组件如何在Application和各种Module之间来回切换?
- 其实就是修改我们的组件的打包方式即可
- 把此处的Application改为library
- 但是如果我们每次都需要手动修改,就比较麻烦了
- 我们可以添加一个开关
- 然后在module中根据开关配置一下即可
if (rootProject.ext.android.is_application) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
- 这个时候,我们把开关设置为false,就会发现,组件都变为Library了
AndroidManifest.xml文件的区分
- 因为application和library的AndroidManifest.xml文件是不同的,这个时候我们就需要区分了
- 我们再module模块中,添加一个manifest文件夹,并拷贝一份manifest文件进去
- 然后library的manifest文件是不需要intent的,这个时候就需要我们去掉intent以及Application里面的部分代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.login">
<application>
<activity android:name=".LoginActivity">
</activity>
</application>
</manifest>
- 我们把每个Module里面的manifest修改了之后,就需要设置一下了,根据is_applitaion属性来动态设置manifest文件
- 在每个module模块下面添加这样一段代码即可
sourceSets{
main{
if (rootProject.ext.android.is_application) {
manifest.srcFile 'src/main/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
}
}
}
library模块中不能有applicationId
- 这个异常是因为什么呢? 是因为我们的defaultConfig中设置了applicationId,但是library中是不需要的
- 怎么解决这个异常呢?
- 我们在设置applicationId的时候添加判断即可
- 如上即可
模块设置完毕了,那么最终我们需要发布的时候,就需要让我们的主App去依赖我们的Module了
- 如上图所示,我们让App依赖下面的两个模块
依赖之后,我们已经发布过了,但是现在我们又需要将我们的模块修改为application怎么办呢?
- 我们改为true,发现抛出了异常
- 异常的意思就是我们的App模块不能依赖我么的login模块和member模块,因为都是application,application不能依赖application模块
- 那么我们就需要修改了,修改成子模块为library的时候,我们才去依赖那两个模块,否则不依赖
- 修改app下面的build.gradle
dependencies {
implementation rootProject.ext.dependencies.publicImplementation
if (!rootProject.ext.android.is_application) {
implementation project(path: ':login')
implementation project(path: ':member')
}
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
以上就是一些基本问题,下面还有一些问题
比如我们在项目中需要知道当前项目是application还是library
- 这个时候就需要我们创建给基础库了
- 为什么需要基础库呢?因为我们每个模块都需要用到这些东西
- 接下来问题来了,基础库创建好了,我们要怎么写才能让每个Module都去依赖它呢?
- 我们先在config中定义一个other库,然后再build.gradle中通过each语法,遍历,依赖即可
- 这样,我们以后就可以只改动一个地方,就好了
- 接下来我们要处理参数传递的问题,我们需要将is_application参数传递给每个模块的buildConfig文件中去
- 我们需要在我们basic模块下的build.config文件中,添加下面的代码,注意各种环境都要添加该属性
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField 'boolean', 'is_application', rootProject.ext.android.is_application.toString()
}
advanced {
buildConfigField 'boolean', 'is_application', rootProject.ext.android.is_application.toString()
}
debug {
buildConfigField 'boolean', 'is_application', rootProject.ext.android.is_application.toString()
}
}
- 接着我们Make一下工程
- 查找basic的buildConfig文件,发现属性已经添加好了
- 接下来我们就可以在basic模块下面使用了
- 注意这里我们需要在basic模块下的application下面声明一个is_application的静态变量
- 这样才能让我们的每一个模块都能使用上这个属性
- 如下
package com.example.basic
import android.app.Application
/**
* <pre>
* author: Jafar
* date : 2020/11/11
* desc :
* </pre>
*/
class BaseApplication : Application() {
companion object {
const val is_application = BuildConfig.is_application
}
override fun onCreate() {
super.onCreate()
}
}
- 当然,我们每一个模块不一定都能用上application,那么我们的baseActivity无疑是最合适的
package com.example.arouterproject
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
/**
* <pre>
* author: Jafar
* date : 2020/11/11
* desc :
* </pre>
*/
class BaseActivity : AppCompatActivity() {
companion object {
const val is_application = BuildConfig.is_application
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
- 我们有时候,需要在模块里面编译的时候加载一些用到的类,但是打包的时候不需要,怎么设置呢?
- 如上图所示,我们只需要创建一个文件夹,把java类拷贝进去,然后在sourceSets中添加这样一行代码即可
- 其实这样一行代码的意思就是声明我这个目录就是一个java的文件夹
- 这样做的好处就是,开发的时候,我们使用Application编译,使用临时的代码进行编译,但是呢?打包的时候,我们就不需要用到了,也就是当其变为library的时候,我们就不需要了。减少了无所谓的编译
- 这种做法,就会防止我们因为不断地切换而产生的BUG
- 解决了资源浪费的问题
- 到此为止。我们就把细节都处理的差不多了