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

前提


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

但是目前由于工作原因嗎,需要和幾個小伙伴一起開發(fā),合作開發(fā),可能不能這樣隨便玩玩了,就需要考慮到合作開發(fā)需要注意的問題,由于項目是剛剛開始,必定要考慮到之后開發(fā)一些坑嗎,打包這個問題,做android的同學(xué),每次遇到都是很無語,項目很大的話可能打包一個需要五六分鐘,太痛苦了,這是后大家應(yīng)該會想到的是插件化開發(fā),隨時隨地的更新app內(nèi)容而不需要打包上線這些流程什么的,但是這個大部分是用于動態(tài)修復(fù)bug和更新模塊,可能會有些偏離我們要做的事情,我們要做到是 代碼耦合度降低,每個模塊完全達到 解耦,不互相牽連,這樣保證了每個人的開發(fā)效率,同時每個module之間也可以打包成相應(yīng)的apk 進行測試

原理


正常我們開發(fā)app的時候在gradle里面配置的主module都是Application,其他的都是Library,那么組件化開發(fā)會有什么區(qū)別的,其實也就是讓每個module運行起來,就是就是把pludgin改成 Application 發(fā)布的時候合并即可

架構(gòu)


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

copy.png

簡要

先說說組件化開發(fā)會遇到的一些問題吧

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

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

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

4.資源名沖突

下面我會一一的說明如何解決這些問題。

project 配置

組件化的基本就是通過 gradle 腳本來做的。

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

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

就是說當(dāng)我們在沒發(fā)布版本之前,我們的每個module之間是相互沒有任何依賴的都可以單獨運行
isDebug這個字段可以在最外層的gradle里面配置,也可以在業(yè)務(wù) module 中放一個 gradle.properties來配置,
但是我個人感覺嗎,最好在外出gradle中配置,這樣每個module 可以用一個總開關(guān)來控制。

下面放置一個完整的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ù)自己的需求進行修改

Manifest

當(dāng)module單獨運行的時候和合并運行的時候每個需要用的manifest也是有些許不同的,一些細微的差別的,但是這個我們也是需要注意的,簡單的一句代碼在gradle重配置即可

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

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

這里有一個小 tip,就是在 release 的 manifest 中,application 標簽下盡量不要放任何東西,只是占個位,讓上面去 merge,否則比如一個 module supportsRtl 設(shè)置為了 true,另一個 module 設(shè)置為了 false,就不得不去做 override 了。

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

這個問題可能每個人會有不同的寫法和解決方法,這里我提供一個簡單的解決方案。
由于我們每個module都會依賴我們的BaseSDK這個library,其實在我們的 BaseSDK中直接定義個BaseApplication即可,然而每個module都可以通過BaseApplication來調(diào)用,這樣就可以解決module與Application之間調(diào)用的問題。代碼如下,可根據(jù)自己的需求不同進行修改

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中可以繼承這個類然后寫一些自己需要初始化的東西
代碼如下:

public class App extends BaseApplication {


    @Override
    public void initSDK() {

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

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

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

這個問題呢,解決方案有很多種 可以自己寫個router來解決跳轉(zhuǎn)之間的問題,也可以借助三方工具來完成這個操作。
自己寫router呢,只不過感覺很有些麻煩,直接上圖吧

屏幕快照 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";
}

就這些自己可以這樣使用。但是我還是推薦使用三方,因為act跳轉(zhuǎn)傳值問題,act請求fragment問題,還有許多未知的坑,所以推薦兩個三方router ARouter,
ActivityRouter,這兩個可以根據(jù)自己需求進行選擇,我用的是ActivityRouter。感覺配置起來會方便許多

ActivityRouter一些配置細節(jié)

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

2.AndroidManifest配置呢,也是如此這個需要配置在需要組件化的module中。而不是主module中,但是如果說是release可以配置在主module中

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

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

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

資源名沖突

這個問題解決最簡單,可以自己命名的時候相互注意一下,也可以在對于的module中的gradle配置

resourcePrefix "preview_"

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

ending

可能前期不會考慮到后面項目逐漸增大了之后 模塊之間的耦合度,需求復(fù)雜度上升等問題,但是組件化開發(fā)的形式可以解耦,降低開發(fā)成本,提高編譯速度,為什么不用呢。何樂而不為。

開開心心上班,安安心心睡覺

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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

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