Android 組件化開(kāi)發(fā)詳解

前提


之前在一直單獨(dú)干位仁,自己隨便搭個(gè)框架就開(kāi)始開(kāi)發(fā)揣钦,such as mvc mvp mvvm clean 一些mv*架構(gòu)吧祭芦,可以隨便弄隨便改移层,方便自己的開(kāi)發(fā)同時(shí)也可以鍛煉自己的架構(gòu)方面的知識(shí)吧仍翰,確實(shí)學(xué)到很多,比如MVP + RxJava + Retrofit + Dagger2 + GreenDAO + Glide 這些結(jié)合起來(lái)用真的讓開(kāi)發(fā)速度提升了很多有想學(xué)習(xí)的同學(xué)可以看看這個(gè)app喜歡的可以關(guān)注下 Life APP

但是目前由于工作原因嗎幽钢,需要和幾個(gè)小伙伴一起開(kāi)發(fā)歉备,合作開(kāi)發(fā),可能不能這樣隨便玩玩了匪燕,就需要考慮到合作開(kāi)發(fā)需要注意的問(wèn)題蕾羊,由于項(xiàng)目是剛剛開(kāi)始喧笔,必定要考慮到之后開(kāi)發(fā)一些坑嗎,打包這個(gè)問(wèn)題龟再,做android的同學(xué)书闸,每次遇到都是很無(wú)語(yǔ),項(xiàng)目很大的話可能打包一個(gè)需要五六分鐘利凑,太痛苦了浆劲,這是后大家應(yīng)該會(huì)想到的是插件化開(kāi)發(fā),隨時(shí)隨地的更新app內(nèi)容而不需要打包上線這些流程什么的哀澈,但是這個(gè)大部分是用于動(dòng)態(tài)修復(fù)bug和更新模塊牌借,可能會(huì)有些偏離我們要做的事情,我們要做到是 代碼耦合度降低割按,每個(gè)模塊完全達(dá)到 解耦膨报,不互相牽連,這樣保證了每個(gè)人的開(kāi)發(fā)效率适荣,同時(shí)每個(gè)module之間也可以打包成相應(yīng)的apk 進(jìn)行測(cè)試

原理


正常我們開(kāi)發(fā)app的時(shí)候在gradle里面配置的主module都是Application现柠,其他的都是Library,那么組件化開(kāi)發(fā)會(huì)有什么區(qū)別的弛矛,其實(shí)也就是讓每個(gè)module運(yùn)行起來(lái)够吩,就是就是把pludgin改成 Application 發(fā)布的時(shí)候合并即可

架構(gòu)


不知道有些同學(xué)開(kāi)過(guò)餓了嗎和滴滴打車發(fā)布的他們的app開(kāi)發(fā)框架,畢竟是大公司丈氓,維護(hù)成本和開(kāi)發(fā)成本都很大周循,他們之前在某it論壇上發(fā)布了一篇文章就是說(shuō)組件化開(kāi)發(fā)架構(gòu),把所有的基礎(chǔ) 所有公共的東西提取出成一個(gè)BaseSDK
然后每個(gè)module依賴這個(gè)Library

copy.png

簡(jiǎn)要

先說(shuō)說(shuō)組件化開(kāi)發(fā)會(huì)遇到的一些問(wèn)題吧

1.module與Application之間調(diào)用的問(wèn)題

2.跨module的Activity 或 Fragment 之間的跳轉(zhuǎn)問(wèn)題

3.AAR 或Library project 重復(fù)依賴

4.資源名沖突

下面我會(huì)一一的說(shuō)明如何解決這些問(wèn)題扒寄。

project 配置

組件化的基本就是通過(guò) gradle 腳本來(lái)做的鱼鼓。

這時(shí)候需要組件化的業(yè)務(wù)module中需要配置

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

就是說(shuō)當(dāng)我們?cè)跊](méi)發(fā)布版本之前,我們的每個(gè)module之間是相互沒(méi)有任何依賴的都可以單獨(dú)運(yùn)行
isDebug這個(gè)字段可以在最外層的gradle里面配置该编,也可以在業(yè)務(wù) module 中放一個(gè) gradle.properties來(lái)配置迄本,
但是我個(gè)人感覺(jué)嗎,最好在外出gradle中配置课竣,這樣每個(gè)module 可以用一個(gè)總開(kāi)關(guān)來(lái)控制嘉赎。

下面放置一個(gè)完整的module 供參考

def Dependencies = rootProject.ext
if (isDebug.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'android-apt'
android {
    compileSdkVersion Dependencies.androidCompileSdkVersion
    buildToolsVersion Dependencies.androidBuildToolsVersion
    resourcePrefix "preview_"
    defaultConfig {
        if (isDebug.toBoolean()) {
            applicationId "com.cuieney.preview"
        }
        minSdkVersion Dependencies.androidMinSdkVersion
        targetSdkVersion Dependencies.androidTargetSdkVersion
        versionCode Dependencies.versionCode
        versionName Dependencies.versionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }



    sourceSets {
        main {
            if (isDebug.toBoolean()) {
                manifest.srcFile 'src/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/release/AndroidManifest.xml'
            }
        }
    }


    packagingOptions {
        exclude 'META-INF/rxjava.properties'
    }

    lintOptions {
        abortOnError Dependencies.abortOnLintError
        checkReleaseBuilds Dependencies.checkReleaseBuilds
        ignoreWarnings Dependencies.ignoreWarnings
    }

    compileOptions {
        sourceCompatibility Dependencies.javaVersion
        targetCompatibility Dependencies.javaVersion
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    repositories {
        flatDir {
            dirs 'libs'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    testCompile 'junit:junit:4.12'
    compile (name: 'StreamingLib', ext: 'aar')
    compile project(':meetvrsdk')
    apt Dependencies.dataDependencies.arouter_compiler
}

可以根據(jù)自己的需求進(jìn)行修改

Manifest

當(dāng)module單獨(dú)運(yùn)行的時(shí)候和合并運(yùn)行的時(shí)候每個(gè)需要用的manifest也是有些許不同的,一些細(xì)微的差別的于樟,但是這個(gè)我們也是需要注意的公条,簡(jiǎn)單的一句代碼在gradle重配置即可

    sourceSets {
        main {
            if (isDebug.toBoolean()) {
                manifest.srcFile 'src/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/release/AndroidManifest.xml'
            }
        }
    }

根據(jù)我們之前全局設(shè)置的isDebug來(lái)進(jìn)行切換manifest即可。main 下的 manifest 寫通用的東西迂曲,另外 2 個(gè)分別寫各自獨(dú)立的靶橱,通常 release 的 manifest 只是一個(gè)空的 application 標(biāo)簽,而 debug 的會(huì)有 application 和調(diào)試用的 activity(你總得要有個(gè)啟動(dòng) activity 吧)及權(quán)限。

這里有一個(gè)小 tip关霸,就是在 release 的 manifest 中传黄,application 標(biāo)簽下盡量不要放任何東西,只是占個(gè)位队寇,讓上面去 merge膘掰,否則比如一個(gè) module supportsRtl 設(shè)置為了 true,另一個(gè) module 設(shè)置為了 false佳遣,就不得不去做 override 了识埋。

module與Application之間調(diào)用的問(wèn)題

這個(gè)問(wèn)題可能每個(gè)人會(huì)有不同的寫法和解決方法,這里我提供一個(gè)簡(jiǎn)單的解決方案零渐。
由于我們每個(gè)module都會(huì)依賴我們的BaseSDK這個(gè)library窒舟,其實(shí)在我們的 BaseSDK中直接定義個(gè)BaseApplication即可,然而每個(gè)module都可以通過(guò)BaseApplication來(lái)調(diào)用诵盼,這樣就可以解決module與Application之間調(diào)用的問(wèn)題辜纲。代碼如下,可根據(jù)自己的需求不同進(jìn)行修改

public abstract class BaseApplication extends Application {
    public static BaseApplication app;
    public static BaseApplication getInstance() {
        return app;
    }
    protected static boolean isDebug = true;

    @Override
    public void onCreate() {
        super.onCreate();
        app = this;
        initSDK();
    }

    public abstract void initSDK();
}

在我們的主application中可以繼承這個(gè)類然后寫一些自己需要初始化的東西
代碼如下:

public class App extends BaseApplication {


    @Override
    public void initSDK() {

        if (LeakCanary.isInAnalyzerProcess(this)) {
            return;
        }
        LeakCanary.install(this);
    }
}

只要把公共需求的東西定義在Base中拦耐,然而調(diào)用的時(shí)候就可以解決這些問(wèn)題

跨module的Activity 或 Fragment 之間的跳轉(zhuǎn)問(wèn)題

這個(gè)問(wèn)題呢,解決方案有很多種 可以自己寫個(gè)router來(lái)解決跳轉(zhuǎn)之間的問(wèn)題见剩,也可以借助三方工具來(lái)完成這個(gè)操作杀糯。
自己寫router呢,只不過(guò)感覺(jué)很有些麻煩苍苞,直接上圖吧

屏幕快照 2017-03-27 下午2.44.57.png

ActivityRouter

public class ActivityRouter {

    public static void startActivity(Context context, String action) {
        context.startActivity(new Intent(action));
    }

    public static void startActivity(Context context, Class clazz) {
        context.startActivity(getIntent(context, clazz));
    }

    public static Intent getIntent(Context context, Class clazz) {
        return new Intent(context, clazz);
    }

    public static void startActivityForName(Context context, String name) {
        try {
            Class clazz = Class.forName(name);
            startActivity(context, clazz);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}

FragmentRouter

public class FragmentRouter {

    public static Fragment getFragment(String name) {
        Fragment fragment;
        try {
            Class fragmentClass = Class.forName(name);
            fragment = (Fragment) fragmentClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return fragment;
    }
}

RouterList

public class RouterList {
    public static final String PREVIEW_MAIN = "com.cuieney.preview.PreviewActivity";
}

就這些自己可以這樣使用固翰。但是我還是推薦使用三方,因?yàn)閍ct跳轉(zhuǎn)傳值問(wèn)題羹呵,act請(qǐng)求fragment問(wèn)題骂际,還有許多未知的坑,所以推薦兩個(gè)三方router ARouter,
ActivityRouter冈欢,這兩個(gè)可以根據(jù)自己需求進(jìn)行選擇歉铝,我用的是ActivityRouter。感覺(jué)配置起來(lái)會(huì)方便許多

ActivityRouter一些配置細(xì)節(jié)

1.ActivityRouter提供的compile可以配置在BaseSDK中凑耻,然后apt配置在需要組件化的module中

2.AndroidManifest配置呢太示,也是如此這個(gè)需要配置在需要組件化的module中。而不是主module中香浩,但是如果說(shuō)是release可以配置在主module中

3.其他的一些配置可以參考ActivityRouter readme

AAR 或Library project 重復(fù)依賴

解決方案各有不同类缤,可以在dependency中根據(jù)isDebug 來(lái)判斷依賴包問(wèn)題等,可以是 將 compile 改為 provided邻吭,只在最終的項(xiàng)目中 compile 對(duì)應(yīng)的代碼餐弱,但是這種辦法只能用于沒(méi)有資源的純代碼工程或者jar包;目前我了解的是這兩種方法 ,大家可以看看還有什么好的辦法提供解決思路

資源名沖突

這個(gè)問(wèn)題解決最簡(jiǎn)單膏蚓,可以自己命名的時(shí)候相互注意一下瓢谢,也可以在對(duì)于的module中的gradle配置

resourcePrefix "preview_"

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

ending

可能前期不會(huì)考慮到后面項(xiàng)目逐漸增大了之后 模塊之間的耦合度,需求復(fù)雜度上升等問(wèn)題翅楼,但是組件化開(kāi)發(fā)的形式可以解耦尉剩,降低開(kāi)發(fā)成本,提高編譯速度毅臊,為什么不用呢理茎。何樂(lè)而不為。

開(kāi)開(kāi)心心上班管嬉,安安心心睡覺(jué)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末皂林,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蚯撩,更是在濱河造成了極大的恐慌础倍,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胎挎,死亡現(xiàn)場(chǎng)離奇詭異沟启,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)犹菇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門德迹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人揭芍,你說(shuō)我怎么就攤上這事胳搞。” “怎么了沼沈?”我有些...
    開(kāi)封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵流酬,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我列另,道長(zhǎng)芽腾,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任页衙,我火速辦了婚禮摊滔,結(jié)果婚禮上阴绢,老公的妹妹穿的比我還像新娘。我一直安慰自己艰躺,他們只是感情好呻袭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著腺兴,像睡著了一般左电。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上页响,一...
    開(kāi)封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天篓足,我揣著相機(jī)與錄音,去河邊找鬼闰蚕。 笑死栈拖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的没陡。 我是一名探鬼主播涩哟,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盼玄!你這毒婦竟也來(lái)了贴彼?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤埃儿,失蹤者是張志新(化名)和其女友劉穎锻弓,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蝌箍,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年暴心,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妓盲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡专普,死狀恐怖悯衬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情檀夹,我是刑警寧澤筋粗,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站炸渡,受9級(jí)特大地震影響娜亿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蚌堵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一买决、第九天 我趴在偏房一處隱蔽的房頂上張望沛婴。 院中可真熱鬧,春花似錦督赤、人聲如沸嘁灯。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)丑婿。三九已至,卻和暖如春没卸,著一層夾襖步出監(jiān)牢的瞬間羹奉,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工办悟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尘奏,地道東北人蝗拿。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓剪撬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親泻轰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子铺然,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理俗孝,服務(wù)發(fā)現(xiàn),斷路器魄健,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • 不怕跌倒赋铝,所以飛翔 組件化開(kāi)發(fā) 參考資源 Android組件化方案 為什么要組件化開(kāi)發(fā) 解決問(wèn)題 實(shí)際業(yè)務(wù)變化非常...
    筆墨Android閱讀 2,968評(píng)論 0 0
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,506評(píng)論 25 707
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,748評(píng)論 6 342
  • 原題 合并兩個(gè)排序的整數(shù)數(shù)組A和B變成一個(gè)新的數(shù)組。 給出A = [1, 2, 3, empty, empty] ...
    Jason_Yuan閱讀 957評(píng)論 0 1