Android組件化設(shè)計(jì)

隨著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文件


project顯示模式下main文件夾

黃色框?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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尊惰,一起剝皮案震驚了整個(gè)濱河市讲竿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌弄屡,老刑警劉巖题禀,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異膀捷,居然都是意外死亡迈嘹,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)秀仲,“玉大人融痛,你說(shuō)我怎么就攤上這事∩窠” “怎么了雁刷?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)挑豌。 經(jīng)常有香客問(wèn)我安券,道長(zhǎng),這世上最難降的妖魔是什么氓英? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任侯勉,我火速辦了婚禮,結(jié)果婚禮上铝阐,老公的妹妹穿的比我還像新娘址貌。我一直安慰自己,他們只是感情好徘键,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布练对。 她就那樣靜靜地躺著,像睡著了一般吹害。 火紅的嫁衣襯著肌膚如雪螟凭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天它呀,我揣著相機(jī)與錄音螺男,去河邊找鬼。 笑死纵穿,一個(gè)胖子當(dāng)著我的面吹牛下隧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谓媒,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼淆院,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了句惯?” 一聲冷哼從身側(cè)響起土辩,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宗弯,沒(méi)想到半個(gè)月后脯燃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蒙保,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了欲主。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邓厕。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡逝嚎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出详恼,到底是詐尸還是另有隱情补君,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布昧互,位于F島的核電站挽铁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏敞掘。R本人自食惡果不足惜叽掘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望玖雁。 院中可真熱鬧更扁,春花似錦、人聲如沸赫冬。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)劲厌。三九已至膛薛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間补鼻,已是汗流浹背哄啄。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辽幌,地道東北人增淹。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像乌企,于是被迫代替她去往敵國(guó)和親虑润。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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