隨著app迭代暴匠,以及功能需求的更新,項(xiàng)目中的冗余代碼量增多房维,代碼各模塊間的耦合增強(qiáng)沼瘫,不利于更新以及測(cè)試,所以開(kāi)始了組建化的設(shè)計(jì)咙俩。
android的組件化基于module的兩種屬性
application屬性耿戚,module可以作為獨(dú)立的app運(yùn)行
apply plugin: ‘com.android.application’
library屬性,module不可獨(dú)立運(yùn)行阿趁,作為依賴庫(kù)文件
apply plugin: ‘com.android.library’
通過(guò)控制module的此屬性可以間module在app以及依賴庫(kù)之間進(jìn)行切換膜蛔,此屬性在build.gradle文件中。
而且項(xiàng)目的gradle文件是可以公用的歌焦,所以可以新建一個(gè)config.gradle文件進(jìn)行對(duì)module以及一些依賴進(jìn)行配置飞几。
/**
* 全局統(tǒng)一配置文件
*/
ext {
//true 每個(gè)業(yè)務(wù)Module可以單獨(dú)開(kāi)發(fā)
//false 每個(gè)業(yè)務(wù)Module以lib的方式運(yùn)行
//修改之后需要Sync方可生效
isModule = false
//版本號(hào)
versions = [
applicationId : "com.example.bwq.moduletest", //應(yīng)用ID
versionCode : 1, //版本號(hào)
versionName : "1.0.0", //版本名稱
compileSdkVersion : 28,
buildToolsVersion : "27.0.3",
minSdkVersion : 16,
targetSdkVersion : 28,
androidSupportSdkVersion: "28.+",
constraintLayoutVersion : "1.1.3",
runnerVersion : "1.0.2",
espressoVersion : "3.0.2",
junitVersion : "4.12",
multidexVersion : "1.0.2",
butterknifeVersion : "8.8.1",
arouterApiVersion : "1.4.0",
arouterCompilerVersion : "1.2.1",
arouterannotationVersion: "1.0.4",
recyclerview : "28.+",
cardview : "28.+",
//三方依賴的版本控制
]
dependencies = ["appcompat_v7" : "com.android.support:appcompat-v7:${versions["androidSupportSdkVersion"]}",
"constraint_layout" : "com.android.support.constraint:constraint-layout:${versions["constraintLayoutVersion"]}",
"runner" : "com.android.support.test:runner:${versions["runnerVersion"]}",
"espresso_core" : "com.android.support.test.espresso:espresso-core:${versions["espressoVersion"]}",
"junit" : "junit:junit:${versions["junitVersion"]}",
"support_annotations" : "com.android.support:support-annotations:${versions["annotationsVersion"]}",
"design" : "com.android.support:design:${versions["androidSupportSdkVersion"]}",
"support-v4" : "com.android.support:support-v4:${versions["androidSupportSdkVersion"]}",
"cardview-v7" : "com.android.support:cardview-v7:${versions["androidSupportSdkVersion"]}",
"recyclerview-v7" : "com.android.support:recyclerview-v7:${versions["androidSupportSdkVersion"]}",
//方法數(shù)超過(guò)65535解決方法64K MultiDex分包方法
"multidex" : "com.android.support:multidex:${versions["multidexVersion"]}",
//路由
"arouter_api" : "com.alibaba:arouter-api:${versions["arouterApiVersion"]}",
"arouter_compiler" : "com.alibaba:arouter-compiler:${versions["arouterCompilerVersion"]}",
"arouter_annotation" : "com.alibaba:arouter-annotation:${versions["arouterannotationVersion"]}",
//黃油刀
"butterknife_compiler": "com.jakewharton:butterknife-compiler:${versions["butterknifeVersion"]}",
"butterknife" : "com.jakewharton:butterknife:${versions["butterknifeVersion"]}",
recyclerview : "com.android.support:recyclerview-v7:${versions["recyclerview"]}",
cardview : "com.android.support:cardview-v7:${versions["cardview"]}",
//三方依賴的庫(kù)地址
]
}
其中isModule為控制module為組建模式還是app模式的開(kāi)關(guān)
因此組建module的屬性可通過(guò)開(kāi)關(guān)來(lái)判斷,在組建build.gradle文件開(kāi)頭設(shè)置為如下:
if (Boolean.valueOf(rootProject.ext.isModule)) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
在組建化中我將app分為三層,
第一層:app層
app入口独撇,初始化頁(yè)面,以及主頁(yè)通過(guò)在主頁(yè)viewpager中添加fragment使其成為正常app的主頁(yè)躁锁。
第二層:組建層
將項(xiàng)目按功能劃分為各個(gè)組建纷铣,如登錄組建,首頁(yè)組建战转,我的組建等搜立,組建間通過(guò)路由的方式跳轉(zhuǎn)以及傳遞數(shù)據(jù)。
第三層:基礎(chǔ)層
項(xiàng)目基礎(chǔ)層槐秧,公用控件啄踊,公用的方法資源文件忧设,網(wǎng)絡(luò)請(qǐng)求方法,以及依賴庫(kù)的封裝都在這層進(jìn)行實(shí)現(xiàn)颠通。
組建層以及app層的每一個(gè)module均依賴基礎(chǔ)層址晕,app層依賴每一個(gè)組建層以及基礎(chǔ)層。
基礎(chǔ)層中將在配置文件中的依賴方法顿锰,進(jìn)行依賴,基礎(chǔ)層不需要成為app獨(dú)立運(yùn)行谨垃,因此無(wú)需設(shè)置application以及l(fā)ibrary的開(kāi)關(guān),依賴關(guān)鍵字使用api:
apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife'
android {
compileSdkVersion rootProject.ext.versions.compileSdkVersion
buildToolsVersion rootProject.ext.versions.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.versions.minSdkVersion
targetSdkVersion rootProject.ext.versions.targetSdkVersion
versionCode rootProject.ext.versions.versionCode
versionName rootProject.ext.versions.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//MultiDex分包方法
multiDexEnabled true
//Arouter路由配置
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
includeCompileClasspath = true
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//把implementation 用api代替,它是對(duì)外部公開(kāi)的, 所有其他的module就不需要添加該依賴
api rootProject.ext.dependencies["appcompat_v7"]
api rootProject.ext.dependencies["constraint_layout"]
api rootProject.ext.dependencies["cardview-v7"]
api rootProject.ext.dependencies["recyclerview-v7"]
api rootProject.ext.dependencies["support-v4"]
api rootProject.ext.dependencies["design"]
api rootProject.ext.dependencies["support_annotations"]
//MultiDex分包方法
api rootProject.ext.dependencies["multidex"]
//黃油刀
annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
api rootProject.ext.dependencies["butterknife"]
//Arouter路由
annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
api rootProject.ext.dependencies["arouter_api"]
api rootProject.ext.dependencies["arouter_annotation"]
api rootProject.ext.dependencies["recyclerview"]
api rootProject.ext.dependencies["cardview"]
}
第二層中因每個(gè)組建均需要成為獨(dú)立app進(jìn)行測(cè)試以及開(kāi)發(fā)硼控,因此刘陶,他的manifest文件需要兩套,一套為普通library的沒(méi)有app入口牢撼,一套為application的有app入口的文件匙隔。
新建module,選擇phone&table module熏版,建立標(biāo)準(zhǔn)app module牡直,main包下新建路徑manifest放置module作為app時(shí)的manifest文件
黃色框?yàn)樾陆ㄎ募A以及新建文件:
manifest配置為基礎(chǔ)app配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bwq.main">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/MyAppTheme"
android:name=".debug.MainApplication">
<activity android:name=".debug.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
紅色框中的manifest文件為library時(shí)的
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bwq.main">
<application>
<activity android:name=".debug.MainActivity"/>
</application>
</manifest>
在java中新建debug包,放置一些作為獨(dú)立app時(shí)的配置纳决,如繼承自基礎(chǔ)層的Application類碰逸,如果組建入口為fragment時(shí)展示fragment的activity,之后在當(dāng)前module的builde.gradle中配置
if (Boolean.valueOf(rootProject.ext.isModule)) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
apply plugin: 'com.jakewharton.butterknife'
android {
compileSdkVersion rootProject.ext.versions.compileSdkVersion
buildToolsVersion rootProject.ext.versions.buildToolsVersion
defaultConfig {
if (Boolean.valueOf(rootProject.ext.isModule))
applicationId rootProject.ext.versions.applicationId
minSdkVersion rootProject.ext.versions.minSdkVersion
targetSdkVersion rootProject.ext.versions.targetSdkVersion
versionCode rootProject.ext.versions.versionCode
versionName rootProject.ext.versions.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//MultiDex分包方法
multiDexEnabled true
//Arouter路由配置
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
includeCompileClasspath = true
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
//配置當(dāng)前module作為app和作為library時(shí)的manifest文件以及作為library時(shí)所需要忽略的文件
main {
if (Boolean.valueOf(rootProject.ext.isModule)) {
manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
exclude '*debug/**'
exclude '*manifest/**'
}
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
//butterKnife
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
api project(':basis_library')
}
為了方便組建間的跳轉(zhuǎn)阔加,在基礎(chǔ)層中BaseApplication類中初始化路由饵史,BaseApplication為所有組建以及app的Application類的基類。
/**
* 初始化ARouter
*/
public void initARouter() {
if (BuildConfig.DEBUG) {
ARouter.openLog();
ARouter.openDebug();
}
ARouter.init(instance);
}
增加組建化后的跳轉(zhuǎn)工具類
public class ARouterUtils {
/**
* 根據(jù)path返回Fragment
*
* @param path path
* @return fragment
*/
public static BaseLazyFragment getLazyFragment(String path) {
return (BaseLazyFragment) ARouter.getInstance()
.build(path)
.navigation();
}
public static BaseFragment getFragment(String path) {
return (BaseFragment) ARouter.getInstance()
.build(path)
.navigation();
}
/**
* 根據(jù)path返回Activity
*
* @param path path
* @return Activity
*/
public static BaseActivity getActivity(String path) {
return (BaseActivity) ARouter.getInstance()
.build(path)
.navigation();
}
/**
* 根據(jù)path返回FragmentActivity
*
* @param path path
* @return FragmentActivity
*/
public static BaseFragmentActivity getFragmentActivity(String path) {
return (BaseFragmentActivity) ARouter.getInstance()
.build(path)
.navigation();
}
}
路由跳轉(zhuǎn)URL地址
/**
* 功能模塊入口
*/
public interface ARouterConfig {
/**
* 登錄頁(yè)面
*/
String LOGIN = "/login/LoginActivity";
/**
* main
*/
String MAIN = "/main/MainFragment";
/**
* mine
*/
String MINE = "/mine/MineFragment";
}
app層:
builde.gradle配置:
apply plugin: 'com.android.application'
apply plugin: 'com.jakewharton.butterknife'
android {
compileSdkVersion rootProject.ext.versions.compileSdkVersion
buildToolsVersion rootProject.ext.versions.buildToolsVersion
defaultConfig {
applicationId rootProject.ext.versions.applicationId
minSdkVersion rootProject.ext.versions.minSdkVersion
targetSdkVersion rootProject.ext.versions.targetSdkVersion
versionCode rootProject.ext.versions.versionCode
versionName rootProject.ext.versions.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//MultiDex分包方法
multiDexEnabled true
//Arouter路由配置
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
includeCompileClasspath = true
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
api project(':basis_library')
if (!Boolean.valueOf(rootProject.ext.isModule)) {
api project(':main')
api project(':login')
api project(':mine')
}
}
至此胜榔,組建化方案完成胳喷。
最后說(shuō)一些注意的問(wèn)題,各層的依賴關(guān)鍵字需要使用api夭织,不同組建間的資源文件由于在組合后可能會(huì)有沖突吭露,各組建間的資源文件建議以組建名開(kāi)頭,如login_back