1. 程式人生 > >Android 元件化必知必會

Android 元件化必知必會

什麼是元件化?

元件化: 就是將一個 Application 的開發分成多個模組,每個模組都是一個元件(Module),開發的過程中我們可以只用專注自己開發的模組,進行單獨除錯,但在最終釋出 apk 時,又可以將這些元件合併成一個統一的 app。
在這裡插入圖片描述

元件化環境搭建

實現元件化基本思路: 根據配置檔案中的 isAloneRun 變數來決定該元件是作為 module 工程整合到 App 工程中,還是單獨作為 app 工程獨立執行。

元件作為 module 和 作為獨立的 App 的不同的配置主要有:

  • 元件的 build.gradle 檔案配置:作為 library 時配置為 apply plugin: 'com.android.library'
    作為 app 時配置為 apply plugin: 'com.android.application'
  • 元件的 AndroidManifest.xml 檔案配置: 作為 library 時,AndroidManifest.xml 檔案中的 標籤不配置 android:applicationName 屬性;
    在這裡插入圖片描述

common_base 模組主要負責封裝公共功能,例如 網路請求框架,圖片載入框架,各種 utils ,為了防止重複依賴庫的問題,所有的依賴庫都放在該模組中進行載入,其他業務元件不做第三方庫的依賴,只需依賴 common_base 公共庫即可。

common_base 模組無論是在什麼情況下都是作為 library 的形式存在的 ,其他的元件依賴它。

第一步: 為了方便管理第三方庫的版本,在 project 目錄下新建一個 config.gradle 檔案(直接複製一份 build.gradle), 配置 SDK 版本和所用到的第三方庫的版本,內容如下:

/**
 引用步驟:
 (1) 在 project 下的 build.gradle 檔案第一行新增 apply from : "config.gradle"
 (2) 在各個 module 中用 implemetions rootProject.ext.dependencies['xxx'] 和 annotationProcessor rootProject.ext..dependencies['xxx']
 **/
ext {
    android = [
            applicationId    : 'com.xing.componentsample',
            compileSdkVersion: 28,
            buildToolsVersion: "28.0.3",
            minSdkVersion    : 15,
            targetSdkVersion : 28,
            versionCode      : 1,
            versionName      : "1.0"
    ]

    dependencies = [
            // support
            "appcompat-v7"        : "com.android.support:appcompat-v7:28.0.0",
            "design"              : "com.android.support:design:28.0.0",
            "support-v4"          : "com.android.support:support-v4:28.0.0",
            "cardview-v7"         : "com.android.support:cardview-v7:28.0.0",
            "annotations"         : "com.android.support:support-annotations:28.0.0",
            "recyclerview-v7"     : "com.android.support:recyclerview-v7:28.0.0",
            "gson"                : "com.google.code.gson:gson:2.2.4",
            "butterknife"         : "com.jakewharton:butterknife:8.8.1",
            "butterknife-compiler": "com.jakewharton:butterknife-compiler:8.8.1",
            "constraint-layout"   : "com.android.support.constraint:constraint-layout:1.1.3",
            "retrofit"            : "com.squareup.retrofit2:retrofit:2.4.0",
            "rxandroid"           : "io.reactivex.rxjava2:rxandroid:2.1.0",
            "rxjava"              : "io.reactivex.rxjava2:rxjava:2.2.2",
            "converter-gson"      : "com.squareup.retrofit2:converter-gson:2.4.0",
            "adapter-rxjava"      : "com.squareup.retrofit2:adapter-rxjava2:2.4.0",
            "okhttp"              : "com.squareup.okhttp3:okhttp:3.11.0",
            "logging-interceptor" : "com.squareup.okhttp3:logging-interceptor:3.11.0",
            "arouter-api"         : "com.alibaba:arouter-api:1.4.1",
            "arouter-compiler"    : "com.alibaba:arouter-compiler:1.2.2"
    ]
}

在 project 中的 build.gradle 檔案中引入該 config.gradle 配置檔案:

apply from: "config.gradle"

第二步: 在 common_base 模組中引入第三方庫:

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    api rootProject.ext.dependencies['appcompat-v7']
    api rootProject.ext.dependencies['design']
    api rootProject.ext.dependencies['support-v4']
    api rootProject.ext.dependencies['cardview-v7']
    api rootProject.ext.dependencies['annotations']
    api rootProject.ext.dependencies['recyclerview-v7']
    api rootProject.ext.dependencies['constraint-layout']
    api rootProject.ext.dependencies['gson']
    api rootProject.ext.dependencies['constraint-layout']
    api rootProject.ext.dependencies['retrofit']
    api rootProject.ext.dependencies['rxandroid']
    api rootProject.ext.dependencies['rxjava']
    api rootProject.ext.dependencies['converter-gson']
    api rootProject.ext.dependencies['adapter-rxjava']
    api rootProject.ext.dependencies['okhttp']
    api rootProject.ext.dependencies['logging-interceptor']
    // butterknife
    api rootProject.ext.dependencies['butterknife']
    annotationProcessor rootProject.ext.dependencies['butterknife-compiler']
    // arouter
    api rootProject.ext.dependencies['arouter-api']
}

注意此處使用 api 而不是 implementions ,否則其他業務元件將引用不到這些第三方庫。

第三步: 在 project 的 gradle.properties 檔案中新增 isRunAlone 變數

# 標示各個業務元件是否以 application 單獨執行,修改後需要同步才能生效
isRunAlone=false

在業務元件的 build.gradle 檔案中,新增 common_base 公共庫的依賴 ,並根據 isRunAlone 變數配置 apply plugin 是 application 還是 library , 以及載入不同的 AndroidManifest.xml 檔案,配置內容如下:

// module_login/build.gradle 檔案
if (isRunAlone.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
apply plugin: 'com.jakewharton.butterknife'
android {
    // 也可以使用這種方式:rootProject.ext.android['compileSdkVersion']
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    buildToolsVersion rootProject.ext.android.buildToolsVersion
    defaultConfig {
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode rootProject.ext.android.versionCode
        versionName rootProject.ext.android.versionName

        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
                includeCompileClasspath = true
            }
        }
    }

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

    sourceSets {
        main {
            if (isRunAlone.toBoolean()) {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                java {
                    // 全部 module 一起編譯時剔除 debug 目錄
                    exclude '**/debug/**'
                }
            }
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    // 依賴公共庫
    implementation project(':common_base')
    // butterknife-compiler
    annotationProcessor rootProject.ext.dependencies['butterknife-compiler']
    // arouter-compiler
    annotationProcessor rootProject.ext.dependencies['arouter-compiler']
}

在這裡插入圖片描述

其中 AndroidManifest.xml檔案的配置,在作為 library 時,不配置 applicationName 和 程式入口 Activity(如果整個 App 的入口 Activity 不是該 Activity )

<!--作為元件module被載入時,Manifest.xml中不能配置 application name -->
<application>
    <activity
        android:name=".activity.LoginActivity"
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name=".activity.RegisterActivity"
        android:screenOrientation="portrait" />
</application>


<!--作為 app 時,需要配置 application name 和程式入口-->
<application
    android:name=".app.LoginModuleApplication"
    android:allowBackup="true"
    android:fullBackupContent="true"
    android:icon="@drawable/login_ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">

    <activity
        android:name=".activity.LoginActivity"
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name=".activity.RegisterActivity"
        android:screenOrientation="portrait" />
</application>

第四步: 各元件之間的跳轉,由於各個元件之間沒有依賴關係,所以通常需要藉助 路由 來做跳轉。

這裡藉助阿里 ARouter 路由;

gradle 配置:

android {
    defaultConfig {
        ........
        // Arouter路由配置
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
                includeCompileClasspath = true
            }
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    // 依賴公共庫
    implementation project(':common_base')
    // arouter-compiler
    annotationProcessor rootProject.ext.dependencies['arouter-compiler']
}

開始跳轉頁面:

/**
 * 跳轉主介面
 */
private void gotoMainActivity() {
    ARouter.getInstance().build("/main/activity").navigation();
    finish();
}

目標頁面:

@Route(path = "/main/activity")
public class MainActivity extends BaseActivity {
    ........
}

整合注意點

ButterKnife 問題

在 project 的 build.gradle 新增 butterknife plugin,然後在所有用到 butterknife 的 library 中新增如下配置

在這裡插入圖片描述

apply plugin: 'com.jakewharton.butterknife'
...........
dependencies {
	// butterknife-compiler
    annotationProcessor rootProject.ext.dependencies['butterknife-compiler']
}

在 library 中整合 butterknife,繫結控制元件時,需要使用 R2 代替 R, 以及在 library 中控制元件的 id 不是常量,所以在不能使用 switch 語句。

在這裡插入圖片描述

在這裡插入圖片描述

資原始檔命名衝突

為了避免資原始檔衝突問題,最好以該模組名為檔案字首進行區分。

在這裡插入圖片描述

原始碼地址: https://github.com/xing16/ComponentSample