組件化開發(fā)

不怕跌倒,所以飛翔

組件化開發(fā)

參考資源

  1. Android組件化方案

為什么要組件化開發(fā)

解決問題

  • 實(shí)際業(yè)務(wù)變化非常快,但是單一工程的業(yè)務(wù)模塊耦合度太高,牽一發(fā)而動(dòng)全身;
  • 對(duì)工程所做的任何修改都必須要編譯整個(gè)工程;
  • 功能測(cè)試和系統(tǒng)測(cè)試每次都要進(jìn)行;
  • 團(tuán)隊(duì)協(xié)同開發(fā)存在較多的沖突.不得不花費(fèi)更多的時(shí)間去溝通和協(xié)調(diào),并且在開發(fā)過(guò)程中,任何一位成員沒辦法專注于自己的功能點(diǎn),影響開發(fā)效率;
  • 不能靈活的對(duì)業(yè)務(wù)模塊進(jìn)行配置和組裝;

好處

  • 加快業(yè)務(wù)迭代速度,各個(gè)業(yè)務(wù)模塊組件更加獨(dú)立,不再出現(xiàn)業(yè)務(wù)耦合情況;
  • 穩(wěn)定的公共模塊采用依賴庫(kù)方式,提供給各個(gè)業(yè)務(wù)線使用,減少重復(fù)開發(fā)和維護(hù)工作量;
  • 迭代頻繁的業(yè)務(wù)模塊采用組件方式,各業(yè)務(wù)研發(fā)可以互不干擾、提升協(xié)作效率,并控制產(chǎn)品質(zhì)量;
  • 為新業(yè)務(wù)隨時(shí)集成提供了基礎(chǔ),所有業(yè)務(wù)可上可下,靈活多變;
  • 降低團(tuán)隊(duì)成員熟悉項(xiàng)目的成本,降低項(xiàng)目的維護(hù)難度;
  • 加快編譯速度,提高開發(fā)效率;
  • 控制代碼權(quán)限,將代碼的權(quán)限細(xì)分到更小的粒度;

具體實(shí)現(xiàn)方案

1.組件模式和集成模式的轉(zhuǎn)換

android studio 中的Module主要又兩種屬性

  • application屬性,可以單獨(dú)運(yùn)行的Android程序,也就是我們的APP

     //這個(gè)是在build.gradle中進(jìn)行設(shè)置的(就是最頂上那一行)
     apply plugin: 'com.android.application'
    
  • library屬性,不可以單獨(dú)運(yùn)行,一般是Android程序依賴的庫(kù)文件

    //這個(gè)是在build.gradle中進(jìn)行設(shè)置的(就是最頂上那一行)
    apply plugin: ‘com.android.library’
    

組件化和集成模式的轉(zhuǎn)換

說(shuō)到這里我不想去講解gradle的語(yǔ)法,因?yàn)槲乙膊粫?huì),但是可以告訴你怎么去弄!!!

這里是引用張華洋的說(shuō)明:博客地址

Module的屬性是在每個(gè)組件的 build.gradle 文件中配置的,當(dāng)我們?cè)诮M件模式開發(fā)時(shí),業(yè)務(wù)組件應(yīng)處于application屬性,這時(shí)的業(yè)務(wù)組件就是一個(gè) Android App,可以獨(dú)立開發(fā)和調(diào)試;而當(dāng)我們轉(zhuǎn)換到集成模式開發(fā)時(shí),業(yè)務(wù)組件應(yīng)該處于 library 屬性,這樣才能被我們的“app殼工程”所依賴,組成一個(gè)具有完整功能的APP;
但是我們?nèi)绾巫尳M件在這兩種模式之間自動(dòng)轉(zhuǎn)換呢?總不能每次需要轉(zhuǎn)換模式的時(shí)候去每個(gè)業(yè)務(wù)組件的 Gralde 文件中去手動(dòng)把 Application 改成 library 吧?如果我們的項(xiàng)目只有兩三個(gè)組件那么這個(gè)辦法肯定是可行的,手動(dòng)去改一遍也用不了多久,但是在大型項(xiàng)目中我們可能會(huì)有十幾個(gè)業(yè)務(wù)組件,再去手動(dòng)改一遍必定費(fèi)時(shí)費(fèi)力,這時(shí)候就需要程序員發(fā)揮下懶的本質(zhì)了。
試想,我們經(jīng)常在寫代碼的時(shí)候定義靜態(tài)常量,那么定義靜態(tài)常量的目的什么呢?當(dāng)一個(gè)常量需要被好幾處代碼引用的時(shí)候,把這個(gè)常量定義為靜態(tài)常量的好處是當(dāng)這個(gè)常量的值需要改變時(shí)我們只需要改變靜態(tài)常量的值,其他引用了這個(gè)靜態(tài)常量的地方都會(huì)被改變,做到了一次改變,到處生效;根據(jù)這個(gè)思想,那么我們就可以在我們的代碼中的某處定義一個(gè)決定業(yè)務(wù)組件屬性的常量,然后讓所有業(yè)務(wù)組件的build.gradle都引用這個(gè)常量,這樣當(dāng)我們改變了常量值的時(shí)候,所有引用了這個(gè)常量值的業(yè)務(wù)組件就會(huì)根據(jù)值的變化改變自己的屬性;可是問題來(lái)了?靜態(tài)常量是用Java代碼定義的,而改變組件屬性是需要在Gradle中定義的,Gradle能做到嗎?
Gradle自動(dòng)構(gòu)建工具有一個(gè)重要屬性,可以幫助我們完成這個(gè)事情。每當(dāng)我們用AndroidStudio創(chuàng)建一個(gè)Android項(xiàng)目后,就會(huì)在項(xiàng)目的根目錄中生成一個(gè)文件 gradle.properties,我們將使用這個(gè)文件的一個(gè)重要屬性:在Android項(xiàng)目中的任何一個(gè)build.gradle文件中都可以把gradle.properties中的常量讀取出來(lái);那么我們?cè)谏厦嫣岬浇鉀Q辦法就有了實(shí)際行動(dòng)的方法,首先我們?cè)趃radle.properties中定義一個(gè)常量值 isModule(是否是組件開發(fā)模式,true為是,false為否):

在gradle.properties中添加是否組件化的判斷

PS:首先gradle.properties這個(gè)文件有一個(gè)重要的功能:在Android項(xiàng)目中的任何一個(gè)build.gradle文件中都可以把gradle.properties中的常量讀取出來(lái)(但是讀取出來(lái)的時(shí)候是String格式的字符串,這一點(diǎn)要切記)

在gradle.properties中添加

# 是否組件化的標(biāo)識(shí),注意啊這里要用"#"號(hào)注釋
isModule = false 

然后在相應(yīng)的Module中的build.gradle去添加相應(yīng)的邏輯

//這里說(shuō)明一下就是如果是組件化的
if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

這樣寫完了之后就會(huì)在每次改變*isModule賦值的化就可以更改相應(yīng)的模塊是否能單獨(dú)運(yùn)行了(為了保證單獨(dú)組件的單獨(dú)運(yùn)行,會(huì)創(chuàng)建一個(gè)公用的Module,存放網(wǎng)絡(luò)請(qǐng)求或者圖片處理等一些公共的資源,從而確保module的單獨(dú)運(yùn)行)

組件之間AndroidManifest合并問題

這里是引用張華洋的說(shuō)明:博客地址

但是大家應(yīng)該注意到這個(gè)問題是在組件開發(fā)模式和集成開發(fā)模式之間轉(zhuǎn)換引起的問題,而在上一節(jié)中我們已經(jīng)解決了組件模式和集成模式轉(zhuǎn)換的問題,另外大家應(yīng)該都經(jīng)歷過(guò)將 Android 項(xiàng)目從 Eclipse 切換到 AndroidStudio 的過(guò)程,由于 Android 項(xiàng)目在 Eclipse 和 AndroidStudio開發(fā)時(shí) AndroidManifest.xml 文件的位置是不一樣的,我們需要在build.gradle 中指定下 AndroidManifest.xml 的位置,AndroidStudio 才能讀取到 AndroidManifest.xml,這樣解決辦法也就有了,我們可以為組件開發(fā)模式下的業(yè)務(wù)組件再創(chuàng)建一個(gè) AndroidManifest.xml,然后根據(jù)isModule指定AndroidManifest.xml的文件路徑,讓業(yè)務(wù)組件在集成模式和組件模式下使用不同的AndroidManifest.xml,這樣表單沖突的問題就可以規(guī)避了。

創(chuàng)建一個(gè)新的AndroidManifest.xml

具體的解決方案就是在相應(yīng)的Module中在根路徑添加一個(gè)Module文件夾,直接創(chuàng)建一份AndroidManifest.xml,這里面的這個(gè)文件,直接像你平時(shí)寫項(xiàng)目一樣去寫就可以了

修改相應(yīng)的build.gradle
在android標(biāo)簽內(nèi)創(chuàng)建
 sourceSets {
        main {
            if (isModule.toBoolean()) {/*是組件化*/
                manifest.srcFile 'src/main/AndroidManifest.xml'
                java {/*集成開發(fā)模式下排除debug文件加中的所有java文件*/
                    exclude 'debug/**'//這個(gè)相應(yīng)的debug文件是在java文件目錄下的
                }
            } else {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            }
        }
    }

全局Context的獲取及組件數(shù)據(jù)初始化

其實(shí)我理解的全局Context的理解就是->先創(chuàng)建一個(gè)所有module都引用的一個(gè)module(common這個(gè)module是每個(gè)module都會(huì)引用的一個(gè)module,這里要處理重復(fù)的問題,后面補(bǔ)充),這個(gè)module就存放一些都能用到的東西,然后引入到module或者主項(xiàng)目中,這樣所有的module都會(huì)包含一些公共的內(nèi)容:如請(qǐng)求網(wǎng)絡(luò),圖片處理等一些相應(yīng)的內(nèi)容,這樣既保證了每個(gè)module的單獨(dú)運(yùn)行,也保證了項(xiàng)目的耦合程度

這里有一個(gè)存在一個(gè)技巧,在前面沒有去理解,就是在組件之間AndroidManifest合并問題處有這樣一段代碼

 java {/*集成開發(fā)模式下排除debug文件加中的所有java文件*/
                    exclude 'debug/**'
                }

這段代碼的主要問題就是解決全局Context的問題,為什么呢???

我們想一下場(chǎng)景啊!當(dāng)你某一個(gè)模塊單獨(dú)運(yùn)行的時(shí)候,存在登陸獲取token的問題,但是你要是單獨(dú)運(yùn)行的時(shí)候呢,可能這個(gè)模塊沒有token你怎么辦呢?其實(shí)解決辦法就是創(chuàng)建一個(gè)debug文件夾,這里呢可以自己創(chuàng)建一個(gè)相應(yīng)的Application去請(qǐng)求登陸的接口這樣你的module單獨(dú)運(yùn)行的時(shí)候就會(huì)有token了具體值
(這里我看張華洋的博客時(shí)我覺的處理挺好的,他在公共的類中去創(chuàng)建了一個(gè)BaseApplication所有module中的Application(在debug文件夾下創(chuàng)建的,但是要在清單文件中去配置啊!!!)都去繼承這個(gè)Application,這樣既解決了沒有Application的情況,又解決了集成打包時(shí)候多次創(chuàng)建Application的情況,覺得比較不錯(cuò)所以借鑒一下)
上面那行代碼會(huì)在集成開發(fā)模式的情況下把debug文件夾下的內(nèi)容刪除,這里就成功的解決了單獨(dú)運(yùn)行的token問題,也解決了集成打包的時(shí)候會(huì)出現(xiàn)多個(gè)Application的問題

library依賴問題

解決library的重復(fù)引用問題又兩種方式:

  • 根據(jù)組件名稱排除
  • 根據(jù)包名排除
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile("com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion") {
            exclude module: 'support-v4'//根據(jù)組件名排除
            exclude group: 'android.support.v4'//根據(jù)包名排除
        }
    }

這里說(shuō)明一點(diǎn):基礎(chǔ)的控件一般都在common中去引用,然后住項(xiàng)目和其他的項(xiàng)目都導(dǎo)入common就可以了,但是這里應(yīng)該存在一個(gè)問題就是Common存在導(dǎo)入多次的問題
其實(shí)在構(gòu)建APP的過(guò)程中Gradle會(huì)自動(dòng)將重復(fù)的arr包排除,APP中也就不會(huì)存在相同的代碼了;

組件之間的調(diào)用和通信

這里是引用張華洋的說(shuō)明:博客地址

在組件化開發(fā)的時(shí)候,組件之間是沒有依賴關(guān)系,我們不能在使用顯示調(diào)用來(lái)跳轉(zhuǎn)頁(yè)面了,因?yàn)槲覀兘M件化的目的之一就是解決模塊間的強(qiáng)依賴問題,
假如現(xiàn)在要從A業(yè)務(wù)組件跳轉(zhuǎn)到業(yè)務(wù)B組件,并且要攜帶參數(shù)跳轉(zhuǎn),這時(shí)候怎么辦呢?
而且組件這么多怎么管理也是個(gè)問題,這時(shí)候就需要引入“路由”的概念了,由本文開始的組件化模型下的業(yè)務(wù)關(guān)系圖可知路由就是起到一個(gè)轉(zhuǎn)發(fā)的作用。
這里我將介紹開源庫(kù)的“ActivityRouter” ,有興趣的同學(xué)情直接去ActivityRouter的Github主頁(yè)學(xué)習(xí):ActivityRouter,ActivityRouter支持給Activity定義 URL,
這樣就可以通過(guò) URL 跳轉(zhuǎn)到Activity,并且支持從瀏覽器以及 APP 中跳入我們的Activity,而且還支持通過(guò) url 調(diào)用方法。
下面將介紹如何將ActivityRouter集成到組件化項(xiàng)目中以實(shí)現(xiàn)組件之間的調(diào)用;

這里涉及到路由的問題后續(xù)在進(jìn)行補(bǔ)充

組件之間資源名稱沖突的問題

這里是引用張華洋的說(shuō)明:博客地址

因?yàn)槲覀儾鸱殖隽撕芏鄻I(yè)務(wù)組件和功能組件,在把這些組件合并到“app殼工程”時(shí)候就有可能會(huì)出現(xiàn)資源名沖突問題,
例如A組件和B組件都有一張叫做“ic_back”的圖標(biāo),這時(shí)候在集成模式下打包APP就會(huì)編譯出錯(cuò),解決這個(gè)問題最簡(jiǎn)單的辦法就是在項(xiàng)目中約定資源文件命名規(guī)約,
比如強(qiáng)制使每個(gè)資源文件的名稱以組件名開始,這個(gè)可以根據(jù)實(shí)際情況和開發(fā)人員制定規(guī)則。當(dāng)然了萬(wàn)能的Gradle構(gòu)建工具也提供了解決方法,
通過(guò)在在組件的build.gradle中添加如下的代碼:

```
//設(shè)置了resourcePrefix值后,所有的資源名必須以指定的字符串做前綴,否則會(huì)報(bào)錯(cuò)。
//但是resourcePrefix這個(gè)值只能限定xml里面的資源,并不能限定圖片資源,所有圖片資源仍然需要手動(dòng)去修改資源名。
resourcePrefix "girls_"
```

但是設(shè)置了這個(gè)屬性后有個(gè)問題,所有的資源名必須以指定的字符串做前綴,否則會(huì)報(bào)錯(cuò),
而且resourcePrefix這個(gè)值只能限定xml里面的資源,并不能限定圖片資源,所有圖片資源仍然需要手動(dòng)去修改資源名;
所以我并不推薦使用這種方法來(lái)解決資源名沖突。

2.組件化項(xiàng)目的工程類型

在組件化工程模型中主要有:app殼工程、業(yè)務(wù)組件和功能組件3種類型,而業(yè)務(wù)組件中的Main組件和功能組件中的Common組件比較特殊,下面將分別介紹。

1.app殼工程

app殼工程相當(dāng)于一個(gè)空殼的項(xiàng)目,沒有任何業(yè)務(wù)代碼,也不能又Activity,但是它又必須單獨(dú)劃分一個(gè)組件,而且不能融合其他組件中;
因?yàn)樗謳c(diǎn)重要的功能:

  • app殼工程中聲明了我們Android應(yīng)用的Application 但是這個(gè)Application必須是繼承Common組件中的BaseApplication(如果無(wú)需事先自己的Application可以直接在表單生命BaseApplication),
    因?yàn)橹挥羞@樣,在打包應(yīng)用后才能讓BaseApplication中的Context生效,當(dāng)然你也可以在這個(gè)Application中初始化我們工程中使用的庫(kù)文件,還可以在這里面解決Android引用方法數(shù)超過(guò)65535的限制,
    對(duì)于崩潰事件的捕獲和發(fā)送也可以在這里面聲明

  • app殼工程的AndroidManifest.xml是我們Android應(yīng)用的根表單 應(yīng)用的名,圖標(biāo)依稀是否支持備份等等屬性都是在這份表單中配置的,
    其他組件中的表單最終在集成開發(fā)模式下都被合并到這份AndroidManifest.xml中

  • app殼工程的build.gradle是比較特殊的 app殼不管是在集成開發(fā)模式還是組件開發(fā)模式,它的屬性始終都是:
    com.android.application,因?yàn)樽罱K其它的組件都要被app殼工程所依賴,被打包進(jìn)app殼工程中,這一點(diǎn)從組件化工程模型圖中就能體現(xiàn)出來(lái),
    所以app殼工程是不需要單獨(dú)調(diào)試單獨(dú)開發(fā)的.另外Android應(yīng)用的打包簽名,以及build.gradle和defaultConfig都需要在這里面配置,
    而它的dependencies則需要根據(jù)isModule的值分別依賴不同的組件,在組件開發(fā)模式下app殼工程只需要依賴Common組件,
    或者為了防止報(bào)錯(cuò)也可以根據(jù)實(shí)際情況依賴其他功能組件,而在集成模式下app殼工程必須依賴所有在應(yīng)用Application中聲明的業(yè)務(wù)組件,并且不需要再依賴任何功能組件。

這里是引用張華洋的說(shuō)明:博客地址

一份工程的build.gradle文件

apply plugin: 'com.android.application'

static def buildTime() {
    return new Date().format("yyyyMMdd");
}

android {
    signingConfigs {
        release {
            keyAlias 'guiying712'
            keyPassword 'guiying712'
            storeFile file('/mykey.jks')
            storePassword 'guiying712'
        }
    }

    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    defaultConfig {
        applicationId "com.guiying.androidmodulepattern"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
        multiDexEnabled false
        //打包時(shí)間
        resValue "string", "build_time", buildTime()
    }

    buildTypes {
        release {
            //更改AndroidManifest.xml中預(yù)先定義好占位符信息
            //manifestPlaceholders = [app_icon: "@drawable/icon"]
            // 不顯示Log
            buildConfigField "boolean", "LEO_DEBUG", "false"
            //是否zip對(duì)齊
            zipAlignEnabled true
            // 縮減resource文件
            shrinkResources true
            //Proguard
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            //簽名
            signingConfig signingConfigs.release
        }

        debug {
            //給applicationId添加后綴“.debug”
            applicationIdSuffix ".debug"
            //manifestPlaceholders = [app_icon: "@drawable/launch_beta"]
            buildConfigField "boolean", "LOG_DEBUG", "true"
            zipAlignEnabled false
            shrinkResources false
            minifyEnabled false
            debuggable true
        }
    }


}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    annotationProcessor "com.github.mzule.activityrouter:compiler:$rootProject.annotationProcessor"
    if (isModule.toBoolean()) {
        compile project(':lib_common')
    } else {
        compile project(':module_main')
        compile project(':module_girls')
        compile project(':module_news')
    }
}

2.功能組件和Common組件

功能組件是為了支撐業(yè)務(wù)組件的某些功能而獨(dú)立劃分出來(lái)的組件,功能組件和第三方庫(kù)是一樣的,
擁有如下特征:

功能組件
  • 功能組件的AndroidManifest.xml是一張空表,這張表只有功能組件的包名;

  • 功能組件不管是在集成開發(fā)還是組件開發(fā)模式下屬性始終是:com.android.library,
    所以功能組件不需要讀取gradle.properties 中的isModule值的;另外功能組件的build.gradle也無(wú)需設(shè)置buildType是,
    只需要dependencies這個(gè)共鞥組件需要的jar包和開源庫(kù).

這里是引用張華洋的說(shuō)明:博客地址

一份 普通 的功能組件的 build.gradle文件:

apply plugin: 'com.android.library'

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion

    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
    }

}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}
Common組件

Common組件除了有功能組件的普遍屬性外,還具有其他功能:

  • Common組件的 AndroidManifest.xml 不是一張空表,這張表中聲明了我們 Android應(yīng)用用到的所有使用權(quán)限 uses-permission 和 uses-feature,放到這里是因?yàn)樵诮M件開發(fā)模式下,所有業(yè)務(wù)組件就無(wú)需在自己的 AndroidManifest.xm 聲明自己要用到的權(quán)限了。
  • Common組件的 build.gradle 需要統(tǒng)一依賴業(yè)務(wù)組件中用到的 第三方依賴庫(kù)和jar包,例如我們用到的ActivityRouter、Okhttp等等。
  • Common組件中封裝了Android應(yīng)用的 Base類和網(wǎng)絡(luò)請(qǐng)求工具、圖片加載工具等等,公用的 widget控件也應(yīng)該放在Common 組件中;業(yè)務(wù)組件中都用到的數(shù)據(jù)也應(yīng)放于Common組件中,例如保存到 SharedPreferences 和 DataBase 中的登陸數(shù)據(jù);
  • Common組件的資源文件中需要放置項(xiàng)目公用的 Drawable、layout、sting、dimen、color和style 等等,另外項(xiàng)目中的 Activity 主題必須定義在 Common中,方便和 BaseActivity 配合保持整個(gè)Android應(yīng)用的界面風(fēng)格統(tǒng)一。

這里是引用張華洋的說(shuō)明:博客地址

一份 Common 的功能組件的 build.gradle文件:

apply plugin: 'com.android.library'

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion

    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
    }

}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    //Android Support
    compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
    compile "com.android.support:design:$rootProject.supportLibraryVersion"
    compile "com.android.support:percent:$rootProject.supportLibraryVersion"
    //網(wǎng)絡(luò)請(qǐng)求相關(guān)
    compile "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
    compile "com.squareup.retrofit2:retrofit-mock:$rootProject.retrofitVersion"
    compile "com.github.franmontiel:PersistentCookieJar:$rootProject.cookieVersion"
    //穩(wěn)定的
    compile "com.github.bumptech.glide:glide:$rootProject.glideVersion"
    compile "com.orhanobut:logger:$rootProject.loggerVersion"
    compile "org.greenrobot:eventbus:$rootProject.eventbusVersion"
    compile "com.google.code.gson:gson:$rootProject.gsonVersion"
    compile "com.github.chrisbanes:PhotoView:$rootProject.photoViewVersion"

    compile "com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion"
    compile "com.github.GrenderG:Toasty:$rootProject.toastyVersion"

    //router
    compile "com.github.mzule.activityrouter:activityrouter:$rootProject.routerVersion"
}

3.業(yè)務(wù)組件和Main組件

業(yè)務(wù)組件

業(yè)務(wù)組件就是根據(jù)業(yè)務(wù)邏輯的不同拆分出來(lái)的組件,業(yè)務(wù)組件的特征如下:

  • 業(yè)務(wù)組件中要有兩張AndroidManifest.xml,分別對(duì)應(yīng)組件開發(fā)模式和集成開發(fā)模式,這兩張表的區(qū)別請(qǐng)查看 組件之間AndroidManifest合并問題 小節(jié)。
  • 業(yè)務(wù)組件在集成模式下是不能有自己的Application的,但在組件開發(fā)模式下又必須實(shí)現(xiàn)自己的Application并且要繼承自Common組件的BaseApplication,并且這個(gè)Application不能被業(yè)務(wù)組件中的代碼引用,因?yàn)樗墓δ芫褪菫榱耸箻I(yè)務(wù)組件從BaseApplication中獲取的全局Context生效,還有初始化數(shù)據(jù)之用。
  • 業(yè)務(wù)組件有debug文件夾,這個(gè)文件夾在集成模式下會(huì)從業(yè)務(wù)組件的代碼中排除掉,所以debug文件夾中的類不能被業(yè)務(wù)組件強(qiáng)引用,例如組件模式下的 Application 就是置于這個(gè)文件夾中,還有組件模式下開發(fā)給目標(biāo) Activity 傳遞參數(shù)的用的 launch Activity 也應(yīng)該置于 debug 文件夾中;
  • 業(yè)務(wù)組件必須在自己的 Java文件夾中創(chuàng)建業(yè)務(wù)組件聲明類,以使 app殼工程 中的 應(yīng)用Application能夠引用,實(shí)現(xiàn)組件跳轉(zhuǎn),具體請(qǐng)查看 組件之間調(diào)用和通信 小節(jié);
  • 業(yè)務(wù)組件必須在自己的 build.gradle 中根據(jù) isModule 值的不同改變自己的屬性,在組件模式下是:com.android.application,而在集成模式下com.android.library;同時(shí)還需要在build.gradle配置資源文件,如 指定不同開發(fā)模式下的AndroidManifest.xml文件路徑,排除debug文件夾等;業(yè)務(wù)組件還必須在dependencies中依賴Common組件,并且引入ActivityRouter的注解處理器annotationProcessor,以及依賴其他用到的功能組件。

這里是引用張華洋的說(shuō)明:博客地址

一份普通業(yè)務(wù)組件的 build.gradle文件:

if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion

    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
    }

    sourceSets {
        main {
            if (isModule.toBoolean()) {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                //集成開發(fā)模式下排除debug文件夾中的所有Java文件
                java {
                    exclude 'debug/**'
                }
            }
        }
    }

    //設(shè)置了resourcePrefix值后,所有的資源名必須以指定的字符串做前綴,否則會(huì)報(bào)錯(cuò)。
    //但是resourcePrefix這個(gè)值只能限定xml里面的資源,并不能限定圖片資源,所有圖片資源仍然需要手動(dòng)去修改資源名。
    //resourcePrefix "girls_"


}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    annotationProcessor "com.github.mzule.activityrouter:compiler:$rootProject.annotationProcessor"
    compile project(':lib_common')
}
Main組件

Main組件集成模式下的AndroidManifest.xml是跟其他業(yè)務(wù)組件不一樣的,Main組件的表單中聲明了我們整個(gè)Android應(yīng)用的launch Activity,這就是Main組件的獨(dú)特之處;所以我建議SplashActivity、登陸Activity以及主界面都應(yīng)屬于Main組件,也就是說(shuō)Android應(yīng)用啟動(dòng)后要調(diào)用的頁(yè)面應(yīng)置于Main組件。

這里是引用張華洋的說(shuō)明:博客地址

一份Main業(yè)務(wù)組件的 build.gradle文件:

        <activity
            android:name=".splash.SplashActivity"
            android:launchMode="singleTop"
            android:screenOrientation="portrait"
            android:theme="@style/SplashTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

3.組件化項(xiàng)目的混淆方案

組件化項(xiàng)目的Java代碼混淆方案采用在集成模式下集中在app殼工程中混淆,各個(gè)業(yè)務(wù)組件不配置混淆文件。 集成開發(fā)模式下在app殼工程中build.gradle文件的release構(gòu)建類型中開啟混淆屬性,其他buildTypes配置方案跟普通項(xiàng)目保持一致,Java混淆配置文件也放置在app殼工程中,各個(gè)業(yè)務(wù)組件的混淆配置規(guī)則都應(yīng)該在app殼工程中的混淆配置文件中添加和修改。
之所以不采用在每個(gè)業(yè)務(wù)組件中開啟混淆的方案,是因?yàn)?組件在集成模式下都被 Gradle 構(gòu)建成了 release 類型的arr包,一旦業(yè)務(wù)組件的代碼被混淆,而這時(shí)候代碼中又出現(xiàn)了bug,將很難根據(jù)日志找出導(dǎo)致bug的原因;另外每個(gè)業(yè)務(wù)組件中都保留一份混淆配置文件非常不便于修改和管理,這也是不推薦在業(yè)務(wù)組件的 build.gradle 文件中配置 buildTypes (構(gòu)建類型)的原因。

4.工程的build.gradle和gradle.properties文件

組件化工程的build.gradle文件

在組件化項(xiàng)目中因?yàn)槊總€(gè)組件的 build.gradle 都需要配置 compileSdkVersion、buildToolsVersion和defaultConfig 等的版本號(hào),而且每個(gè)組件都需要用到 annotationProcessor,為了能夠使組件化項(xiàng)目中的所有組件的 build.gradle 中的這些配置都能保持統(tǒng)一,并且也是為了方便修改版本號(hào),我們統(tǒng)一在Android工程根目錄下的build.gradle中定義這些版本號(hào),當(dāng)然為了方便管理Common組件中的第三方開源庫(kù)的版本號(hào),最好也在這里定義這些開源庫(kù)的版本號(hào),然后在各個(gè)組件的build.gradle中引用Android工程根目錄下的build.gradle定義的版本號(hào),組件化工程的 build.gradle 文件代碼如下:

這里是引用張華洋的說(shuō)明:博客地址

buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }

    dependencies {
        //classpath "com.android.tools.build:gradle:$localGradlePluginVersion"
        //$localGradlePluginVersion是gradle.properties中的數(shù)據(jù)
        classpath "com.android.tools.build:gradle:$localGradlePluginVersion"
    }
}

allprojects {
    repositories {
        jcenter()
        mavenCentral()
        //Add the JitPack repository
        maven { url "https://jitpack.io" }
        //支持arr包
        flatDir {
            dirs 'libs'
        }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

// Define versions in a single place
//時(shí)間:2017.2.13;每次修改版本號(hào)都要添加修改時(shí)間
ext {
    // Sdk and tools
    //localBuildToolsVersion是gradle.properties中的數(shù)據(jù)
    buildToolsVersion = localBuildToolsVersion
    compileSdkVersion = 25
    minSdkVersion = 16
    targetSdkVersion = 25
    versionCode = 1
    versionName = "1.0"
    javaVersion = JavaVersion.VERSION_1_8

    // App dependencies version
    supportLibraryVersion = "25.3.1"
    retrofitVersion = "2.1.0"
    glideVersion = "3.7.0"
    loggerVersion = "1.15"
    eventbusVersion = "3.0.0"
    gsonVersion = "2.8.0"
    photoViewVersion = "2.0.0"

    //需檢查升級(jí)版本
    annotationProcessor = "1.1.7"
    routerVersion = "1.2.2"
    easyRecyclerVersion = "4.4.0"
    cookieVersion = "v1.0.1"
    toastyVersion = "1.1.3"
}

組件化工程的gradle.properties文件

在組件化實(shí)施流程中我們了解到gradle.properties有兩個(gè)屬性對(duì)我們非常有用:

  • 在Android項(xiàng)目中的任何一個(gè)build.gradle文件中都可以把gradle.properties中的常量讀取出來(lái),不管這個(gè)build.gradle是組件的還是整個(gè)項(xiàng)目工程的build.gradle;
  • gradle.properties中的數(shù)據(jù)類型都是String類型,使用其他數(shù)據(jù)類型需要自行轉(zhuǎn)換;

利用gradle.properties的屬性不僅可以解決集成開發(fā)模式和組件開發(fā)模式的轉(zhuǎn)換,而且還可以解決在多人協(xié)同開發(fā)Android項(xiàng)目的時(shí)候,因?yàn)殚_發(fā)團(tuán)隊(duì)成員的Android開發(fā)環(huán)境(開發(fā)環(huán)境指Android SDK和AndroidStudio)不一致而導(dǎo)致頻繁改變線上項(xiàng)目的build.gradle配置。

在每個(gè)Android組件的 build.gradle 中有一個(gè)屬性:buildToolsVersion,表示構(gòu)建工具的版本號(hào),這個(gè)屬性值對(duì)應(yīng) AndroidSDK 中的 Android SDK Build-tools,正常情況下 build.gradle 中的 buildToolsVersion 跟你電腦中 Android SDK Build-tools 的最新版本是一致的,比如現(xiàn)在 Android SDK Build-tools 的最新的版本是:25.0.3,那么我的Android項(xiàng)目中 build.gradle 中的 buildToolsVersion 版本號(hào)也是 25.0.3,但是一旦一個(gè)Android項(xiàng)目是由好幾個(gè)人同時(shí)開發(fā),總會(huì)出現(xiàn)每個(gè)人的開發(fā)環(huán)境 Android SDK Build-tools 是都是不一樣的,并不是所有人都會(huì)經(jīng)常升級(jí)更新 Android SDK,而且代碼是保存到線上環(huán)境的(例如使用 SVN/Git 等工具),某個(gè)開發(fā)人員提交代碼后線上Android項(xiàng)目中 build.gradle 中的 buildToolsVersion 也會(huì)被不斷地改變。

另外一個(gè)原因是因?yàn)锳ndroid工程的根目錄下的 build.gradle 聲明了 Android Gradle 構(gòu)建工具,而這個(gè)工具也是有版本號(hào)的,而且 Gradle Build Tools 的版本號(hào)跟 AndroidStudio 版本號(hào)一致的,但是有些開發(fā)人員基本很久都不會(huì)升級(jí)自己的 AndroidStudio 版本,導(dǎo)致團(tuán)隊(duì)中每個(gè)開發(fā)人員的 Gradle Build Tools 的版本號(hào)也不一致。

如果每次同步代碼后這兩個(gè)工具的版本號(hào)被改變了,開發(fā)人員可以自己手動(dòng)改回來(lái),并且不要把改動(dòng)工具版本號(hào)的代碼提交到線上環(huán)境,這樣還可以勉強(qiáng)繼續(xù)開發(fā);但是很多公司都會(huì)使用持續(xù)集成工具(例如Jenkins)用于持續(xù)的軟件版本發(fā)布,而Android出包是需要 Android SDK Build-tools 和 Gradle Build Tools 配合的,一旦提交到線上的版本跟持續(xù)集成工具所依賴的Android環(huán)境構(gòu)建工具版本號(hào)不一致就會(huì)導(dǎo)致Android打包失敗。

為了解決上面問題就必須將Android項(xiàng)目中 build.gradle 中的 buildToolsVersion 和 GradleBuildTools 版本號(hào)從線上代碼隔離出來(lái),保證線上代碼的 buildToolsVersion 和 Gradle Build Tools 版本號(hào)不會(huì)被人為改變。

具體的實(shí)施流程大家可以查看這篇博文: AndroidStudio本地化配置gradle的buildToolsVersion和gradleBuildTools

上面這些要感謝章華洋的博客,基本是按照他的寫法自己寫了一遍,體會(huì)了一下,里面的只是還是很多了,僅作備份,如果樓主有意見,馬上刪除!!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,595評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,814評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,224評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,444評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,988評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,804評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,998評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,237評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評(píng)論 1 287
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,706評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,993評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,665評(píng)論 25 708
  • 1. Android組件化開發(fā) 在Android項(xiàng)目組件化之前,我們的項(xiàng)目都是像下圖那樣,一個(gè)單一工程下,根據(jù)不同...
    CHSmile閱讀 4,557評(píng)論 1 34
  • 前因 其實(shí)我們這個(gè)7人iOS開發(fā)團(tuán)隊(duì)并不適合組件化開發(fā)。原因是因?yàn)樾詢r(jià)比低,需要花很多時(shí)間和經(jīng)歷去做這件事,帶來(lái)的...
    曹俊_413f閱讀 4,211評(píng)論 6 29
  • 愛情心理學(xué)中說(shuō),我們?cè)诿CH撕V形í?dú)對(duì)他或者她鐘情,其實(shí)是潛意識(shí)鼓動(dòng)我們這樣做。潛意識(shí)的形成主要在青年期,每個(gè)人的...
    李梓妍Li閱讀 804評(píng)論 2 3
  • 金針輕盈源本色, 盡展清莼拽春還。 孤芳自賞安不見, 芬菲內(nèi)斂拒斗艷。 水綠天藍(lán)湖山抱, 南華飽潤(rùn)六祖壇。 草原沙...
    T悅兒閱讀 1,486評(píng)論 0 0