組件化入門——手把手寫一個Demo

組件化開發就是基於可重用的目的,將一個大的軟件系統分離,拆分成多個獨立的組件。組件可以是模塊、web資源、軟件包等。

最近想知道組件化是什麼東西,於是看了很多博客,設計到很多理論知識,但是越看越懵。還不如找個Demo學一下,學的過程又遇到很多新的東西,特此記錄一下!順便也可以作爲新手入門組件化的文章(不要臉- -)。。。我覺得看再多還不如跟着寫一個Demo來的實際點。

所以,開始吧!

我們先像平常一樣創建一個工程ComponentDemo!它的目錄結構應該差不多是這樣的:

app作爲一個主模塊,我們只在裏面放一個ComponentApplication:

這個Demo 我們使用 阿里巴巴 的 ARouter 來實現組件之間的跳轉,有什麼區別呢?

1、比如我們在 ModuleA 想要使用傳統的 Intent 啓動 ModuleB 的一個Activity,就需要讓 ModuleA 依賴 ModuleB。

2、如果我們使用 ARouter,只需要在主Module(一般是app模塊)中依賴所有需要使用到的 Module,比如在 app模塊中依賴 ModuleA 和 ModuleB,我們的 ModuleA 就可以跳轉到 ModuleB,而 ModuleA 不需要依賴 ModuleB。

接下來我們選中工程,右鍵,新建一個Module,取名c-common,用來存放第三方庫的依賴、資源文件、統一SDK。也就是說,c-common 作爲 library,其他的 Module 只要在build.gradle中依賴 c-common,就能使用 c-common 裏面的資源和第三方庫,不需要重複依賴,也能避免依賴衝突,資源文件的衝突等。

java目錄中可以存放工具類util和其他通用的類,res目錄存放各種資源、style等,build.gradle用來依賴第三方庫等。接下來我們來寫c-common中的build.gradle:

apply plugin: 'com.android.library'

android {
    compileSdkVersion rootProject.ext.compileSdkVersion

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

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

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

}

dependencies {
    api "com.android.support:appcompat-v7:${rootProject.supportLibraryVersion}"
    api "com.android.support.constraint:constraint-layout:${rootProject.constraintVersion}"
    api "junit:junit:${rootProject.junitVersion}"
    api "com.android.support.test:runner:${rootProject.testRunnerVersion}"
    api "com.android.support.test.espresso:espresso-core:${rootProject.espressocoreVersion}"

    api "com.alibaba:arouter-api:${rootProject.arouterApiVersion}"
}

因爲我們的 c-common 是作爲 library 來使用,要把第一行改爲 apply plugin: 'com.android.library',接着配置 compileSdkVersion、各種Version、還有各種依賴等。

注意:在dependencies中使用 api 和以前經常用的 compile 效果是一樣的。另外使用 implementation 依賴配置,會顯著提升構建時間。兩者的區別如下:
api/compile: 如有模塊之間存在依賴的話,引用是正常的。
implementation: 引用的庫只能在當前模塊中使用,即便模塊之間存在依賴關係的話,也不可以引用。

這裏我們在 Project下 的 build.gradle 中定義一些變量,這樣在模塊中就可以引用這些變量了。看一下 Project下 的 build.gradle 

buildscript {
    ...
}
allprojects {
    repositories {
        google()
        jcenter()
    }
}
task clean(type: Delete) {
    delete rootProject.buildDir
}
ext{
    compileSdkVersion = 28
    minSdkVersion = 15
    targetSdkVersion = 28
    versionCode = 1
    versionName = "1.0"

    supportLibraryVersion = "28.0.0"
    constraintVersion = "1.1.3"
    junitVersion = "4.12"
    testRunnerVersion = "1.0.2"
    espressocoreVersion = "3.0.2"
    arouterApiVersion = "1.3.1"
    annotationProcessor = "1.1.4"
}

這裏添加了 ext 塊,在裏面定義 SDK 版本 和各個依賴的版本。然後可以在各個Module 中通過 rootProject.ext.xxx引用他們。

然後我們看一下 c-common 的 AndroidManifest.xml ,由於 c-common 是一個 library ,所以它不需要註冊 Application,也沒有 Activity。但是我們可以在這裏統一聲明需要的權限,只要其他 Module 依賴 c-common,就相當於自己也聲明瞭權限 :

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.c_common">
    //聲明權限
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
</manifest>

c-common 中還可以添加各種工具類,這裏我就不演示了。

還有style、colors、圖片等也可以在 c-common 中,這裏就不贅述了。

接下來,我們再添加一個 c-main 的 Module ,在這裏寫一個默認啓動的Activity:

我們看一下 c-main 的 build.gradle:

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

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        if(isDebug.toBoolean()){
            applicationId "com.example.c_main"
        }
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [moduleName: project.getName()]
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(":c-common")
}

這裏我們在 工程 下的 gradle.properties 中定義了一個變量 isDebug。

Module 可以在 Debug 模式下作爲單獨的 Application 運行,在 Release 模式下作爲 Library 運行。Gradle構建的工程中,用 apply plugin: 'com.android.application' 來標識該爲 Application ,而 apply plugin: 'com.android.library' 標誌位 Library .因此,我們可以在編譯的是同通過判斷構建環境中的參數來修改子工程的工作方式,在 c-main 的 build.gradle 裏面加了個判斷,當 isDebug 爲 false 的時候,也就是我們整個應用作爲整體運行時, c-main 這個Module 作爲一個library。

我們繼續看 c-main 的 build.gradle 。當 c-main 作爲 Library 運行時,它是不能持有 applicationId 的,所以加了如下判斷

c-main 還引用了 c-common,意味着 c-main 不用重複依賴庫,方便了不少!

接下來我們看一下 c-main 的 AndroidManifest.xml 文件:

這裏 application 我們不指定,我們統一使用 app 模塊中的 Application ,這樣也可以統一在 Application中做初始化,後面會看到。
既然是個應用,那就需要一個啓動Activity,這裏我們將我們的MainActivity 指定爲啓動Activity。

然後再 c-main 的 res 目錄下,我們刪除 values 目錄下的 styles 和 colors 文件,因爲我們只需要統一用 c-common 中的 styles和colors就可以了,這裏不刪除會產生衝突。

接下來我們嘗試運行整個應用,但是在此之前我們還需要一個標誌爲 Application 的模塊,我們把 app 模塊標誌爲 Application

我們配置一下 app 模塊:

我們先看 app 模塊的build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    defaultConfig {
        applicationId "com.example.hp.componentdemo"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        multiDexEnabled true
    }
    signingConfigs{
        release{}
        debug {}
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
        debug {
            signingConfig signingConfigs.debug
        }
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    implementation 'com.android.support:multidex:1.0.1'

    if(!isDebug.toBoolean()){
        implementation project(":c-main")
        implementation project(":c-login")
    }
}

把 app 模塊標誌爲 Application,就需要加上 apply plugin: 'com.android.application'。

然後 app 模塊作爲 Application,要運行就需要一個默認的 啓動Activity,也就是 c-main 中的 MainActivity,就需要依賴 c-main 。

還有一點,我們整個應用只用一個 Application類,當然你也可以每個模塊都有一個Application類,這裏我們只討論前者,我們需要在 app模塊 的 AndroidManifest.xml 中配置 Application:

需要指定 android:name 屬性。

接下來我們就可以運行整個應用了。

接下來我們通過登錄按鈕來跳轉到登錄模塊,我們先寫一個 c-login 模塊:

c-login 的 build.gradle:

if(isDebug.toBoolean()){
    apply plugin: 'com.android.application'
}else{
    apply plugin: 'com.android.library'
}
android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        if(isDebug.toBoolean()){
            applicationId "com.example.c_login"
        }
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [moduleName: project.getName()]
            }
        }
    }

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

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(':c-common')
    if(!isDebug.toBoolean()){
        annotationProcessor "com.alibaba:arouter-compiler:${rootProject.annotationProcessor}"
    }
}

這裏的配置大致和 c-main 差不多,但是多了兩處,

我們從 c-main 模塊 想要使用 ARouter 跳轉到 c-login 模塊中的Activity,就需要在 c-login 中添加這兩處地方,且需要在 app 模塊的build.gradle中依賴 c-login,且在app 模塊的Application中進行初始化

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

    implementation project(':c-common')

    if(!isDebug.toBoolean()){
        implementation project(":c-main")
        implementation project(":c-login")
    }
}
public class ComponentApplication extends Application {
    private static final String TAG = "ComponentApplication";

    @Override
    public void onCreate() {
        super.onCreate();
        initRouter();
    }

    private void initRouter() {
        if(BuildConfig.DEBUG){
            ARouter.openDebug();
            ARouter.openLog();
        }
        ARouter.init(this);
    }
}

相關 ARouter的配置 https://blog.csdn.net/baidu_21345205/article/details/80360774

接下來看 c-login的 AndroidManifest.xml:

同樣不需要指定application,統一使用 app模塊中的 Application。還有刪除 values目錄下的styles 和 colors,這裏不贅述了。

然後再MainActivity中就可以跳轉到 LoginActivity 了:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btLogin = findViewById(R.id.bt_login);
        btLogin.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if(v.getId()== R.id.bt_login){
            ARouter.getInstance()
                    .build("/login/activity")
                    .navigation();
        }
    }
}

點擊按鈕後

ARouter 的應用就差不多這樣了。

運行單個組件

這裏我們配置並運行 c-login 

我們先置 isDebug 爲 true,

從 c-login 的gradle文件中前幾行可以看到,當 isDebug 爲 true 時,它會作爲一個 application 運行

既然作爲一個application運行,那就需要一個默認啓動的 Activity,我們在 c-login/src/main 下創建一個文件夾 module,並新建一個 AndroidManifest.xml:

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.c_login">

    <application
        android:name="debug.MainApplication"
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="DemoCC"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat.Light.NoActionBar">
        <activity android:name=".LoginActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

這裏指定LoginActivity爲默認啓動的Activity。這裏如果需要進行某些初始化,可以 c-login/src/main/java目錄下新建一個 debug 目錄,然後繼承 Application,進行初始化,並在Manifest 中註冊。

我們需要使用在 debug的時候需要使用這個特性的 AndroidManifest.xml:

需要在 c-login 的 build.gradle 中指定:

...

android {
    ...
    sourceSets{
        main {
            if(isDebug.toBoolean()){
                manifest.srcFile("src/main/module/AndroidManifest.xml")
            }else{
                manifest.srcFile("src/main/AndroidManifest.xml")
                java {
                    //排除debug目錄下的文件
                    exclude 'debug/**'
                }
            }
        }
    }

}

...

然後選擇 c-login 運行

成功運行組件!

這篇文章到這裏就結束了!

 

這裏記錄幾點重要的:

1.防止資源文件衝突,只在common中配置style等,其他模塊刪除,否則無法預覽且報錯。

2.只有application才能配置applicationId,library不能配置applicationId

3.不是debug模式的時候,app模塊作爲application,需要一個啓動的Activity,這時候就需要在app的gradle中引入該模塊

4.library中的資源id不是final類型的,不能用switch,只能用if

5.多 Module 開發注意問題:https://www.cnblogs.com/bhm666/p/8010820.html

 

哈勒,文章結束!喜歡點個贊~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章