前言:這篇模塊化與組件化的文章,用2篇文章介紹。可能有些人以爲網上已經有了文章,爲何還要寫。第一:爲了記錄本身的正常也算當作筆記。第二:網上當然有好文,但最近看了一篇竟然有150多贊,可是介紹的迷迷糊糊,不少知識點略過。本文重點是讓你快速入門,理解以及使用。java
本次模塊化/組件化講解總共分2篇(必須先了解ARouter,或第三方路由框架):
一、阿里路由框架ARouter的基本使用
二、Android中經過對gradle的管理實現組件化;並配合ARouter,隨意跳轉切換android
其實在沒了解過模塊化/組件化以前,我以爲很是高端,甚至不敢觸碰。多是由於其餘人的博客很高端。其實接觸後發現,其實就是經過gradle的管理實現的。高端的只不過是各模塊module之間的通訊,但是ARouter已經幫咱們解決了全部事情。跟着我一步一步。一塊兒實現,末尾加上本文demo。git
在本文文章開始前,先講個小知識點:
A module 引入了B module,B module引入了C module,若是使用的是implementation方式,那麼C對於A來講是不可見的;而使用api方式C是能夠被A訪問的。同理,把C換成開源庫、so文件、aar文件、jar包文件結論也適用。 若是是jar包的,父類要引入子類的話必需要加上路勁如: dirs 'libs', '../moduleB/libs' 。父類引入moduleB中的libsgithub
這裏也和大部分網上同樣,以微信爲例。把微信分爲4個模塊module:home、chat、search、mine。api
經過對gradle配置開光的更改,咱們能夠單獨把home模塊,chat模塊,search模塊,mine模塊,自身做爲app運行。這樣模塊化的好處是,解耦。把各模塊給開發人員開發,互不影響,且代碼管理也不會起衝突等等。微信
這些app的生成,只是改變gradle的配置開光便可生成。網絡
完整app | home_module單獨運行app | chat_module單獨運行app |
---|---|---|
![]() |
![]() |
![]() |
從最外層看 | ||
![]() |
新建項目,我這裏的項目是CatModuleStu,在項目build.gradle的目錄下,new一個config.gradle。不會的直接複製項目build.gradle,改下名字便可。裏面的內容呢,根據咱們的app裏build.gradle進行更改,app裏的build.gradle以下:app
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.lihang.catmodulestu"
minSdkVersion 22
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' } 複製代碼
意思就是把這些具體的引用用常量表示,每一個module都使用這些常量,因此咱們的confing.gradle以下:框架
ext {
//這裏是配置開關。是否須要單獨運行。注意,這裏只能打開一個。由於一次只能運行一個。。
//true 表示須要單獨運行。false表示不須要單獨運行。
isNeedHomeModule = false
isNeedChatModule = false
isNeedFindModule = false
isNeedMineModule = false
android = [
compileSdkVersion: 28,
buildToolsVersion: "28.0.0",
minSdkVersion : 22,
targetSdkVersion : 28,
versionCode : 1,
versionName : "1.0.0",
applicationId : "com.lihang.catmodulestu",
applicationHomeId: "com.lihang.homemodule",
applicationChatId: "com.lihang.chatmodule"
]
//這個對依賴庫版本version的管理,就更加細緻化了
version = [
androidSupportSdkVersion: "28.0.0"
]
//系統依賴
dependencies = [
"support:appcompat-v7": "com.android.support:appcompat-v7:${version["androidSupportSdkVersion"]}",
"test:runner" : 'com.android.support.test:runner:1.0.2',
"test.espresso" : 'com.android.support.test.espresso:espresso-core:3.0.2',
"junit" : 'junit:junit:4.12'
]
//第三方庫(請原諒個人英語)
//這樣依賴庫看起來比較清晰(dependencies : 表明系統依賴庫;thridencies表明第三依賴庫)
thridencies = [
"butterknife" : 'com.jakewharton:butterknife:8.8.1',
"butterknife-compiler": 'com.jakewharton:butterknife-compiler:8.8.1',
"arouter-compiler" : 'com.alibaba:arouter-compiler:1.1.4',
"arouter" : 'com.alibaba:arouter-api:1.3.1',
]
}
複製代碼
這些寫好以後,在咱們的項目build.gradle最頂部,引用一下咱們的config.gradle: apply from: "config.gradle"ide
作完這些後,在來看咱們的app的build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion rootProject.ext.android["compileSdkVersion"]
defaultConfig {
applicationId rootProject.ext.android["applicationId"]
minSdkVersion rootProject.ext.android["minSdkVersion"]
targetSdkVersion rootProject.ext.android["targetSdkVersion"]
versionCode rootProject.ext.android["versionCode"]
versionName rootProject.ext.android["versionName"]
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation rootProject.ext.dependencies["support:appcompat-v7"] testImplementation rootProject.ext.dependencies["junit"] androidTestImplementation rootProject.ext.dependencies["test:runner"] androidTestImplementation rootProject.ext.dependencies["test.espresso"] } 複製代碼
新建baseModule,把經常使用的網絡請求,圖片加載,意思就是其餘module共用的東西放進去.包括共用的資源文件,BaseActivity,BaseFragment和application也放在這,上篇說的ARouter的使用,初始化也放在這。這個baseModule是徹底做爲library的。被其餘module和app引用。可是這裏有2個坑:
一、引入ARouter的時候,在baseModule的build.gradle裏dependencies標籤下加上:
api rootProject.ext.thridencies["arouter"]
annotationProcessor rootProject.ext.thridencies["arouter-compiler"]
複製代碼
在android的defaultConfig標籤加上
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
複製代碼
其餘module引用baseModule的時候,一樣也要在defaultConfig標籤下加上javaCompileOptions。同時還要引入以下依賴,對!你沒有看錯。若是不加,那麼將不成功。:
annotationProcessor rootProject.ext.thridencies["arouter-compiler"]
複製代碼
二、這裏用butterknife。好比baseModule引用了butterknife後,其餘module引用baseModule的時候會出現個bug、錯誤提示:元素必須爲常量。而後butterknife官方提供了一個處理方案,用R2。可是在切換module做爲app運行的時候,R2會報錯,意思做爲app的時候R2要改爲R。假如你module裏都用了R2,這個時候要所有改爲R。因此不建議baseModule引用butterknife。若有好的解決方法,望告知!!
首先看homeModule的build.gradle:
//這裏就用到了config的配置isNeedHomeModule
//開頭設置,若是打開開光,當成項目運行,不然當成library引用
if (isNeedHomeModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
compileSdkVersion rootProject.ext.android["compileSdkVersion"]
defaultConfig {
minSdkVersion rootProject.ext.android["minSdkVersion"]
targetSdkVersion rootProject.ext.android["targetSdkVersion"]
if (isNeedHomeModule.toBoolean()) {
//同時在conifg.gradle配置上homeModule的包名。
//看成爲application運行的時候,給他配置上獨立的包名
applicationId rootProject.ext.android["applicationHomeId"]
}
versionCode rootProject.ext.android["versionCode"]
versionName rootProject.ext.android["versionName"]
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//ARouter的使用記得要加哦
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
buildTypes {
release {
minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets {
main {
if (isNeedHomeModule.toBoolean()) {
//這裏目前的作法是2套AndroidManifest,做爲app運行的時候要指定啓動頁
manifest.srcFile 'src/main/buildApp/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/buildModule/AndroidManifest.xml'
}
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation rootProject.ext.dependencies["support:appcompat-v7"] testImplementation rootProject.ext.dependencies["junit"] androidTestImplementation rootProject.ext.dependencies["test:runner"] androidTestImplementation rootProject.ext.dependencies["test.espresso"] implementation project(':baseModule') annotationProcessor rootProject.ext.thridencies["arouter-compiler"] } 複製代碼
從上代碼中咱們能夠看到有3點注意的地方:
if (isNeedHomeModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
複製代碼
if (isNeedHomeModule.toBoolean()) {
applicationId rootProject.ext.android["applicationHomeId"]
}
複製代碼
if (isNeedHomeModule.toBoolean()) {
//這裏目前的作法是2套AndroidManifest,做爲app運行的時候要指定啓動頁
manifest.srcFile 'src/main/buildApp/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/buildModule/AndroidManifest.xml'
}
複製代碼
這裏2個AndroidManifest就不貼了,能夠經過個人demo本身去看。其實就是指定一個啓動頁Activity。我這裏爲了demo的清晰,就新建了個SelectHomeActivity,而後就是homeModule模塊,用到的全部Activity配置什麼的都放在這裏。若是是做爲library,也是把全部的配置放在這。被主app引用的時候,他會自動來這裏找的,不用咱們擔憂。
作完這些,當homeModule做爲app運行的時候,咱們主app固然也要判斷,就引用不了homeModule了,build.gradle以下:
dependencies {
... //省略部分代碼,便於理解
if (!isNeedHomeModule.toBoolean()) {
implementation project(':homeModule') } } 複製代碼
作完這些,就完成了。其餘module也是和這個一樣的配置。經過更改config.gradle的配置isNeedHomeModule,就能夠模塊單獨之間運行了。
當成library引用後。好比我這個demo是點擊下方按鈕進行切換。由於咱們使用了ARouter,就能夠這樣(固然這裏引用,你也能夠直接用類名):
//這樣就生成了一個HomeFragment的實例
HomeFragemnt fragment_one = (HomeFragemnt) ARouter.getInstance().build(Constance.FRAGMENT_HOME_PATH).withString("wo", "1").navigation();
複製代碼
這裏我說個我本身的理解:
模塊化:就像這樣,把咱們的「微信」,分爲4個模塊。我理解爲模塊化。
組件化:就像這裏的baseModule,或者你封裝的dialog,popwindow。再好比,你用的網上第三方的一些效果。我理解爲模塊化。和模塊化同樣,都是爲了解耦。