组件化开发就是基于可重用的目的,将一个大的软件系统分离,拆分成多个独立的组件。组件可以是模块、web资源、软件包等。
最近想知道组件化是什么东西,于是看了很多博客,设计到很多理论知识,但是越看越懵。还不如找个Demo学一下,学的过程又遇到很多新的东西,特此记录一下!顺便也可以作为新手入门组件化的文章(不要脸- -)。。。我觉得看再多还不如跟着写一个Demo来的实际点。
所以,开始吧!
我们先像平常一样创建一个工程ComponentDemo!它的目录结构应该差不多是这样的:
app作为一个主模块,我们只在里面放一个ComponentApplication:
这个Demo 我们使用 阿里巴巴 的 ARouter 来实现组件之间的跳转,有什么区别呢?
1、比如我们在 ModuleA 想要使用传统的 Intent 启动 ModuleB 的一个Activity,就需要让 ModuleA 依赖 ModuleB。
2、如果我们使用 ARouter,只需要在主Module(一般是app模块)中依赖所有需要使用到的 Module,比如在 app模块中依赖 ModuleA 和 ModuleB,我们的 ModuleA 就可以跳转到 ModuleB,而 ModuleA 不需要依赖 ModuleB。
接下来我们选中工程,右键,新建一个Module,取名c-common,用来存放第三方库的依赖、资源文件、统一SDK。也就是说,c-common 作为 library,其他的 Module 只要在build.gradle中依赖 c-common,就能使用 c-common 里面的资源和第三方库,不需要重复依赖,也能避免依赖冲突,资源文件的冲突等。
java目录中可以存放工具类util和其他通用的类,res目录存放各种资源、style等,build.gradle用来依赖第三方库等。接下来我们来写c-common中的build.gradle:
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
lintOptions {
abortOnError false
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
api "com.android.support:appcompat-v7:${rootProject.supportLibraryVersion}"
api "com.android.support.constraint:constraint-layout:${rootProject.constraintVersion}"
api "junit:junit:${rootProject.junitVersion}"
api "com.android.support.test:runner:${rootProject.testRunnerVersion}"
api "com.android.support.test.espresso:espresso-core:${rootProject.espressocoreVersion}"
api "com.alibaba:arouter-api:${rootProject.arouterApiVersion}"
}
因为我们的 c-common 是作为 library 来使用,要把第一行改为 apply plugin: 'com.android.library',接着配置 compileSdkVersion、各种Version、还有各种依赖等。
注意:在dependencies中使用 api
和以前经常用的 compile
效果是一样的。另外使用 implementation
依赖配置,会显著提升构建时间。两者的区别如下:
api/compile: 如有模块之间存在依赖的话,引用是正常的。
implementation: 引用的库只能在当前模块中使用,即便模块之间存在依赖关系的话,也不可以引用。
这里我们在 Project下 的 build.gradle 中定义一些变量,这样在模块中就可以引用这些变量了。看一下 Project下 的 build.gradle
buildscript {
...
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
ext{
compileSdkVersion = 28
minSdkVersion = 15
targetSdkVersion = 28
versionCode = 1
versionName = "1.0"
supportLibraryVersion = "28.0.0"
constraintVersion = "1.1.3"
junitVersion = "4.12"
testRunnerVersion = "1.0.2"
espressocoreVersion = "3.0.2"
arouterApiVersion = "1.3.1"
annotationProcessor = "1.1.4"
}
这里添加了 ext 块,在里面定义 SDK 版本 和各个依赖的版本。然后可以在各个Module 中通过 rootProject.ext.xxx引用他们。
然后我们看一下 c-common 的 AndroidManifest.xml ,由于 c-common 是一个 library ,所以它不需要注册 Application,也没有 Activity。但是我们可以在这里统一声明需要的权限,只要其他 Module 依赖 c-common,就相当于自己也声明了权限 :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.c_common">
//声明权限
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
</manifest>
c-common 中还可以添加各种工具类,这里我就不演示了。
还有style、colors、图片等也可以在 c-common 中,这里就不赘述了。
接下来,我们再添加一个 c-main 的 Module ,在这里写一个默认启动的Activity:
我们看一下 c-main 的 build.gradle:
if(isDebug.toBoolean()){
apply plugin: 'com.android.application'
}else{
apply plugin: 'com.android.library'
}
android {
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
if(isDebug.toBoolean()){
applicationId "com.example.c_main"
}
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(":c-common")
}
这里我们在 工程 下的 gradle.properties 中定义了一个变量 isDebug。
Module 可以在 Debug 模式下作为单独的 Application 运行,在 Release 模式下作为 Library 运行。Gradle构建的工程中,用 apply plugin: 'com.android.application' 来标识该为 Application ,而 apply plugin: 'com.android.library' 标志位 Library .因此,我们可以在编译的是同通过判断构建环境中的参数来修改子工程的工作方式,在 c-main 的 build.gradle 里面加了个判断,当 isDebug 为 false 的时候,也就是我们整个应用作为整体运行时, c-main 这个Module 作为一个library。
我们继续看 c-main 的 build.gradle 。当 c-main 作为 Library 运行时,它是不能持有 applicationId 的,所以加了如下判断
c-main 还引用了 c-common,意味着 c-main 不用重复依赖库,方便了不少!
接下来我们看一下 c-main 的 AndroidManifest.xml 文件:
这里 application 我们不指定,我们统一使用 app 模块中的 Application ,这样也可以统一在 Application中做初始化,后面会看到。
既然是个应用,那就需要一个启动Activity,这里我们将我们的MainActivity 指定为启动Activity。
然后再 c-main 的 res 目录下,我们删除 values 目录下的 styles 和 colors 文件,因为我们只需要统一用 c-common 中的 styles和colors就可以了,这里不删除会产生冲突。
接下来我们尝试运行整个应用,但是在此之前我们还需要一个标志为 Application 的模块,我们把 app 模块标志为 Application
我们配置一下 app 模块:
我们先看 app 模块的build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.example.hp.componentdemo"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
signingConfigs{
release{}
debug {}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
debug {
signingConfig signingConfigs.debug
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:multidex:1.0.1'
if(!isDebug.toBoolean()){
implementation project(":c-main")
implementation project(":c-login")
}
}
把 app 模块标志为 Application,就需要加上 apply plugin: 'com.android.application'。
然后 app 模块作为 Application,要运行就需要一个默认的 启动Activity,也就是 c-main 中的 MainActivity,就需要依赖 c-main 。
还有一点,我们整个应用只用一个 Application类,当然你也可以每个模块都有一个Application类,这里我们只讨论前者,我们需要在 app模块 的 AndroidManifest.xml 中配置 Application:
需要指定 android:name 属性。
接下来我们就可以运行整个应用了。
接下来我们通过登录按钮来跳转到登录模块,我们先写一个 c-login 模块:
c-login 的 build.gradle:
if(isDebug.toBoolean()){
apply plugin: 'com.android.application'
}else{
apply plugin: 'com.android.library'
}
android {
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
if(isDebug.toBoolean()){
applicationId "com.example.c_login"
}
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':c-common')
if(!isDebug.toBoolean()){
annotationProcessor "com.alibaba:arouter-compiler:${rootProject.annotationProcessor}"
}
}
这里的配置大致和 c-main 差不多,但是多了两处,
我们从 c-main 模块 想要使用 ARouter 跳转到 c-login 模块中的Activity,就需要在 c-login 中添加这两处地方,且需要在 app 模块的build.gradle中依赖 c-login,且在app 模块的Application中进行初始化
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation project(':c-common')
if(!isDebug.toBoolean()){
implementation project(":c-main")
implementation project(":c-login")
}
}
public class ComponentApplication extends Application {
private static final String TAG = "ComponentApplication";
@Override
public void onCreate() {
super.onCreate();
initRouter();
}
private void initRouter() {
if(BuildConfig.DEBUG){
ARouter.openDebug();
ARouter.openLog();
}
ARouter.init(this);
}
}
相关 ARouter的配置 https://blog.csdn.net/baidu_21345205/article/details/80360774
接下来看 c-login的 AndroidManifest.xml:
同样不需要指定application,统一使用 app模块中的 Application。还有删除 values目录下的styles 和 colors,这里不赘述了。
然后再MainActivity中就可以跳转到 LoginActivity 了:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btLogin = findViewById(R.id.bt_login);
btLogin.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if(v.getId()== R.id.bt_login){
ARouter.getInstance()
.build("/login/activity")
.navigation();
}
}
}
点击按钮后
ARouter 的应用就差不多这样了。
运行单个组件
这里我们配置并运行 c-login
我们先置 isDebug 为 true,
从 c-login 的gradle文件中前几行可以看到,当 isDebug 为 true 时,它会作为一个 application 运行
既然作为一个application运行,那就需要一个默认启动的 Activity,我们在 c-login/src/main 下创建一个文件夹 module,并新建一个 AndroidManifest.xml:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.c_login">
<application
android:name="debug.MainApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="DemoCC"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity android:name=".LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
这里指定LoginActivity为默认启动的Activity。这里如果需要进行某些初始化,可以 c-login/src/main/java目录下新建一个 debug 目录,然后继承 Application,进行初始化,并在Manifest 中注册。
我们需要使用在 debug的时候需要使用这个特性的 AndroidManifest.xml:
需要在 c-login 的 build.gradle 中指定:
...
android {
...
sourceSets{
main {
if(isDebug.toBoolean()){
manifest.srcFile("src/main/module/AndroidManifest.xml")
}else{
manifest.srcFile("src/main/AndroidManifest.xml")
java {
//排除debug目录下的文件
exclude 'debug/**'
}
}
}
}
}
...
然后选择 c-login 运行
成功运行组件!
这篇文章到这里就结束了!
这里记录几点重要的:
1.防止资源文件冲突,只在common中配置style等,其他模块删除,否则无法预览且报错。
2.只有application才能配置applicationId,library不能配置applicationId
3.不是debug模式的时候,app模块作为application,需要一个启动的Activity,这时候就需要在app的gradle中引入该模块
4.library中的资源id不是final类型的,不能用switch,只能用if
5.多 Module 开发注意问题:https://www.cnblogs.com/bhm666/p/8010820.html
哈勒,文章结束!喜欢点个赞~