Android組件化實(shí)踐

前言

相信各位小伙伴們對(duì)組件化開發(fā)都不陌生了,本文只對(duì)我所理解和使用的組件化開發(fā)方案做一個(gè)總結(jié),有不正確或者需要改進(jìn)和優(yōu)化的地方,望大家及時(shí)指出

如何將一個(gè)項(xiàng)目組件化

下面以我的ModuleDemo舉例說明

架構(gòu)圖

image

大致分為4部分:

1. App殼

啟動(dòng)頁,組件初始化,項(xiàng)目基本框架

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

具體的組件,例如上圖home,moduleA組件

3. 組件基礎(chǔ)庫

例如上圖business
定義了各個(gè)組件對(duì)外提供的服務(wù),組件間的共享資源

4. 通用庫,路由庫

全局通用的資源(例如Theme和color等),第三方庫
路由庫主要用戶業(yè)務(wù)組件間交互

調(diào)試和發(fā)布

為了避免每個(gè)組件都進(jìn)行重復(fù)的一些配置,我這里對(duì)gradle做了些封裝,下面會(huì)給完整的配置,這里我們先來看看大致步驟

工程 gradle配置

apply from: "${rootProject.rootDir}/version.gradle"

統(tǒng)一依賴,組件模式開關(guān)

App gradle配置

dependencies {
    implementation rootProject.ext.dependencies["appcompat-v7"]
    if (rootProject.ext.isModuleADebug){
        implementation project(':modulea')
    }
    if (rootProject.ext.isHomeDebug){
        implementation project(':home')
    }
}

組件單獨(dú)運(yùn)行時(shí),取消app的依賴

組件 gradle配置

模式切換
apply from: "${rootProject.rootDir}/config.gradle"
def isHomeDebug = rootProject.ext.isHomeDebug
if (rootProject.ext.isHomeDebug) {
    project.ext.setLibDefaultConfig project
} else {
    project.ext.setAppDefaultConfig project
}

通過控制isHomeDebug的值,來導(dǎo)入不同的配置,調(diào)試模式引入Application插件,和設(shè)置Application相關(guān)的配置,組件模式引入library插件,設(shè)置library相關(guān)配置

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

如圖

image

當(dāng)組件作為單獨(dú)的project運(yùn)行時(shí),我們需要提供調(diào)試相關(guān)一些資源,所以我創(chuàng)建了兩個(gè)文件夾,module文件夾里面提供AndroidManifest為了調(diào)試模式時(shí)的啟動(dòng)頁配置,debug文件夾可以放置調(diào)試時(shí)需要使用的資源,例如Activity等

UI跳轉(zhuǎn)

組件內(nèi)部,采用原生的跳轉(zhuǎn)方式,不同組件間采用Arouter中的Provider來提供組件的相關(guān)跳轉(zhuǎn),我覺得這樣可以很清楚的知道每個(gè)模塊對(duì)外提供的功能,比起在模塊上的Activity上加注解跳轉(zhuǎn)方式好一些

在business組件基礎(chǔ)庫中,定義home組件的接口

/**
 * home模塊對(duì)外暴露功能
 */
public interface IHomeProvider extends IProvider {
    void goHome(Context context);
}

在home組件中實(shí)現(xiàn)該接口

@Route(path = RouterHelper.ROUTER_HOME_PROVIDER)
public class HomeProviderImpl implements IHomeProvider {
    @Override
    public void init(Context context) {
        Log.d("HomeProviderImpl", "home初始化");
    }

    @Override
    public void goHome(Context context) {
        Intent intent = new Intent(context, HomeActivity.class);
        context.startActivity(intent);
    }
}

跳轉(zhuǎn)

((IHomeProvider) RouterHelper.getModule(RouterHelper.ROUTER_HOME_PROVIDER)).goHome(MainActivity.this);

組件通信

組件間都是相互獨(dú)立的,他們之間不存在任何依賴.沒有依賴,就無法產(chǎn)生關(guān)系,沒有關(guān)系就無法傳遞任何的信息.這個(gè)時(shí)候我們需要依賴第三方協(xié)助我們,也就是業(yè)務(wù)基礎(chǔ)庫,所有的業(yè)務(wù)組件都依賴該庫,通過它來進(jìn)行通信.
我們可以采用事件總線的方式,例如EventBus,將在業(yè)務(wù)基礎(chǔ)庫中定義需要傳遞的消息對(duì)象,在業(yè)務(wù)組件中訂閱該消息,通過這種方式我們就可以實(shí)現(xiàn)不同組件中的消息通信,當(dāng)然我們同樣也可以在上面提到的Provider方式,不過我覺得這兩種方式?jīng)]啥區(qū)別

注意點(diǎn)

組件化過程中還是有許多需要注意的地方,這里主要說三個(gè)地方

1. 依賴沖突

每個(gè)組件都可能引入自己的庫,這樣合并的時(shí)候就會(huì)產(chǎn)生沖突,解決這個(gè)沖突的方式就是統(tǒng)一依賴管理,后面會(huì)給出詳細(xì)配置

2. 不要使用butterknife

相信很多人都喜歡使用這個(gè)庫,但是組件化中最好還是不要使用了,雖然它提供了R2這種方式解決了lib中的id問題,但是每次單獨(dú)運(yùn)行的時(shí)候得手動(dòng)改成R,個(gè)人覺得有點(diǎn)麻煩,再說findviewbyid也花不了多少時(shí)間,一個(gè)插件的就搞定的事,也不容易出問題

3. 資源沖突

如果A和B組件中都有同一個(gè)名字的資源文件,在集成模式的時(shí)候打包就會(huì)報(bào)錯(cuò),可以使用在gradle中配置resourcePrefix 來解決

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

當(dāng)然也可以團(tuán)隊(duì)約定好代碼規(guī)范,那樣更好

詳細(xì)配置

version.gradle

ext {
    isHomeDebug = true
    isModuleADebug = true

    android = [
            applicationId    : "com.hc.moduledemo",
            compileSdkVersion: 27,
            minSdkVersion    : 19,
            targetSdkVersion : 22,
            versionCode      : 1,
            versionName      : "1.0",
            supportVersion   : "27.1.1"
    ]
    dependencies = [
            "arouter"     : ["arouter-api"     : "com.alibaba:arouter-api:1.4.1",
                             "arouter-compiler": "com.alibaba:arouter-compiler:1.2.2"],
            "appcompat-v7": "com.android.support:appcompat-v7:${android.supportVersion}"
    ]

}

config.gradle

//所有業(yè)務(wù)模塊通用的一些配置
ext {
    //設(shè)置App配置
    setAppDefaultConfig = {
        extension ->
            extension.apply plugin: 'com.android.application'
            extension.description "app"
            setAndroidConfig extension.android
            setDependencies extension.dependencies
            extension.android.defaultConfig {//設(shè)置applicationId
                applicationId rootProject.ext.android.applicationId + "." + extension.getName()
            }
    }

    //設(shè)置Lib配置
    setLibDefaultConfig = {
        extension ->
            extension.apply plugin: 'com.android.library'
            extension.description "lib"
            setAndroidConfig extension.android
            setDependencies extension.dependencies
    }

    //設(shè)置Android配置
    setAndroidConfig = {
        extension ->
            extension.compileSdkVersion rootProject.ext.android.compileSdkVersion
            extension.defaultConfig {
                minSdkVersion rootProject.ext.android.minSdkVersion
                targetSdkVersion rootProject.ext.android.targetSdkVersion
                testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
                javaCompileOptions {
                    annotationProcessorOptions {
                        arguments = [AROUTER_MODULE_NAME: extension.project.getName()]
                    }
                }
            }
    }

    //設(shè)置依賴
    setDependencies = {
        extension ->
            extension.implementation fileTree(dir: 'libs', include: ['*.jar'])
            extension.testImplementation 'junit:junit:4.12'
            extension.androidTestImplementation 'com.android.support.test:runner:1.0.2'
            extension.androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
            extension.annotationProcessor rootProject.ext.dependencies["arouter"]["arouter-compiler"]
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 編譯時(shí)間越來越長,時(shí)間=生命,我要救命。 項(xiàng)目框架 最開始項(xiàng)目只有一個(gè)app,項(xiàng)目結(jié)構(gòu)很簡單,就是一個(gè)業(yè)務(wù)modu...
    展翅而飛閱讀 4,601評(píng)論 5 18
  • Android組件化項(xiàng)目地址:Android組件化項(xiàng)目AndroidModulePattern Android組件...
    半灬邊灬天閱讀 3,003評(píng)論 4 37
  • 不怕跌倒,所以飛翔 組件化開發(fā) 參考資源 Android組件化方案 為什么要組件化開發(fā) 解決問題 實(shí)際業(yè)務(wù)變化非常...
    筆墨Android閱讀 3,096評(píng)論 0 0
  • 一、知識(shí)詳解模塊 1.dex/class深入講解 2.jvm/dvm/art三個(gè)虛擬機(jī)的深入講解 3.class ...
    hanfengzqh閱讀 4,905評(píng)論 0 20
  • 最近實(shí)在是太忙了,專業(yè)課搞不下來,所以不打算在寫文章了,但是我可以做摘抄,都看見有人在推薦書單,所以我就把我自己最...
    ranchongwei閱讀 325評(píng)論 0 0

友情鏈接更多精彩內(nèi)容