1. 程式人生 > >Android元件化開發實踐

Android元件化開發實踐

前兩年安卓外掛化開發在國內進行的如火如荼,出現了不少外掛化的框架,360,攜程等都開源了比較好的外掛化框架,外掛化開發確實是可以給開發者帶來很大的方便。不過外掛化需要解決的問題太多,動態載入類,資源等等,首先就需要對安卓的打包機制,執行機制等等了解的很清楚。對個人的技術要求非常高,這些開源框架或多或少都會有一些bug,在我們改框架的實現機制不理解的情況下,出現的一些bug會讓我們手無足措。而且對於這種可以動態更改線上apk的框架谷歌是不推薦的,不經過稽核的東西總是存在一些風險。
外掛化解決的問題:
(1)不同的模組分開開發
(2)動態更新apk。
元件化開發
(1)不同的模組分開開發
相對來說元件化比外掛化簡單很多,首先對於線上的apk不會有任何影響,不會有不可控的問題出現,所有的問題編譯的時候就會暴露出來。
我理解的元件化開發:


(1)有一個library模組,裡面包含了我們日常開發用到的通用工具,如網路框架,圖片框架,各種util等等。
(2)每個業務模組都依賴library可以用library中的公共工具。
(3)每個模組之間沒有耦合,可以隨時加入或者撤除每個模組
(4)每個業務模組可以單獨的除錯,這樣當我們的app已經很大的時候,編譯一次是很耗時的,單獨除錯每個模組會增大我們的開發效率。
(5)多個業務模組可以多個團隊同時開發,單獨除錯。
(6)單獨測試摸個模組會更加簡單。
所以如果要使用元件化開發,首先需要跟我們的app的業務結合,我們的app需要有很多的不同業務模組,加入只有很少的業務或者業務相同,我感覺也沒必要使用元件化。

下面開始實踐:
既然是元件化開發當然有多個元件啦,假如我們有兩個獨立的業務模組《天氣模組》《段子模組》。OK撿完工程後的目錄結構:
這裡寫圖片描述

module_library:包含各種工具,網路請求,圖片處理,各種util
module_duanzi:段子模組
module_weather:天氣模組
依賴關係就是: app依賴 module_duanzi和module_weather ,module_duanzi和module_weather都依賴module_library。不用擔心studio打包的時候是不會重複依賴的

既然我們建了這麼多的模組

首先遇到的第一個問題就是每個模組的build.gradle檔案中的各種版本如編譯版本,sdk版本啊等等,需要統一的版本號,這個時候就用到了一個全域性的檔案 gradle.properties 這個檔案中的配置我們可以在所有的build.gradle中訪問到。就如同static 靜態變數。
所以gradle.properties中就可以這麼寫

isDebug= false
# 編譯的 SDK 版本
COMPILE_SDK_VERSION=25
# 構建工具的版本,其中包括了打包工具aapt、dx等,如API20對應的build-tool的版本就是20.0.0
BUILDTOOLS_VERSION=26.0.0
# 支援包的版本
SUPPORT_LIB_VERSION=25.3.0
# 最小支援sdk
MIN_SDK_VERSION=18
# 當前targetSdkVersion sdk
TARGET_SDK_VERSION=25
# butterknife 版本
BUTTERKNIFE_VERDION = 8.8.0
# Arouter
A_ROUTER_API=1.2.1.1
A_ROUTER_COMPILER=1.1.2.1
#A_ROUTER_ANNOTATION=1.0.3

然後在build.gradle中呼叫 ,部分程式碼如下:

    compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)
    buildToolsVersion BUILDTOOLS_VERSION
    minSdkVersion Integer.parseInt(MIN_SDK_VERSION)
    targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)
    compile "com.android.support:appcompat-v7:${SUPPORT_LIB_VERSION}"
    compile "com.jakewharton:butterknife:${BUTTERKNIFE_VERDION}"
    annotationProcessor "com.jakewharton:butterknife-compiler:${BUTTERKNIFE_VERDION}"
    compile "com.alibaba:arouter-api:${A_ROUTER_API}"
    annotationProcessor "com.alibaba:arouter-compiler:${A_ROUTER_COMPILER}"

這樣當版本變化的時候,只需更改gradle.properties中的版本號所有的module中的版本號都可以改變

第二個問題元件之間怎麼跳轉。元件之沒有聯絡,主工程依賴元件,可以通過intent跳轉,假如哪天真有一個硬性的問題需要元件之間需要元件之間通訊就無法做到了。而主工程跟元件通過intent連線,不能做到很好的隔離。這時候可以使用路由(路由的原理就跟我們請求一個網頁一樣,根據一個網址就可以跳轉過去了,路由也是,我們可以給我我們的activity配置一個路徑,別的地方就可以根據這個路徑訪問到我們的activity) 路由框架可以使用阿里的ARouter https://github.com/alibaba/ARouter。具體使用方法點選前面的連結,進入 ARouter專案github主頁,有詳細介紹。
這樣我們在每個module的入口activity上配置訪問路徑

@Route(path = "/duanzi/DuanZiActivity")
public class DuanZiHuActivity extends BaseActivity {}
@Route(path = "/weather/WeatherActivity")
public class WeatherActivity extends BaseActivity {}

在主工程中一句話跳轉就可以啦

@OnClick({R.id.weather,R.id.zhihu})
    public void onViewClicked(View view) {
        switch (view.getId()){
            case R.id.weather:
                ARouter.getInstance().build("/weather/WeatherActivity").navigation();
                break;
            case R.id.zhihu:
                ARouter.getInstance().build("/duanzi/DuanZiActivity").navigation();
                break;
        }
    }

元件內的跳轉頁可以通過ARouter實現啦。不過我覺得還是當我們元件之間跳轉的時候使用,或者使用ARouter 的特定的場景(比如我們APP沒的推廣的介面,活動介面等複雜場景),元件內部還是使用intent。

第三個問題 ButterKnife的使用

ButterKnife在github上的star現在有一萬八千多。使用的人更多啦,我們一般是在我們的主工程中使用,那ButterKnife在library中怎麼用呢 進入ButterKnife的github主頁https://github.com/JakeWharton/butterknife作者已經給出了方法
這裡寫圖片描述

可以看到在library中使用R2來引用。使用的時候我們可以通過正常的方法或使用外掛生成程式碼後把R手動改成R2之後重新編譯一下工程就好啦。

這裡需要注意:
ARouter 和 ButterKnife都使用了編譯時註解機制。

annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

根據實踐,每個使用到他們功能的模組都需要在build.gradle 寫上面的註解管理器的依賴。

第四個問題 防止資源命名重複
module多了之後,每個module中都有自己的資原始檔,為了防止命名的重複,我們可以在build.gradle中配置resourcePrefix引數,強制命名的字首。
比如我們在module_weather的build.gradle檔案的android下面的defaultConfig中新增

resourcePrefix "weather_"

代表這個module中的檔案的命名都要以weather_為字首 。

第五個問題 元件的單獨除錯
模組化開發的很大的一個優點就是元件的單獨除錯了,那麼怎麼單獨除錯呢。
我們看到 app模組的build,gradle檔案的最上面是

apply plugin: 'com.android.application'

而module模組的build,gradle 最上面是

apply plugin: 'com.android.library'

說明 app模組是一個正常的app工程,module模組只是一個app的library,所以只有app可以執行,正常情況下我們把module中的library改成application 就可以把它變成一個app工程了。但是一個app的執行需要有一個入口activity啊。我們的 module的manifest中沒有入口activity啊。當然我們可以給它加入一個入口activity,但是如果我們的module模組很多的話,每次都這麼改多累啊而且容易出錯。這個時候就得配置一下啦。

還記的最開始的時候說的gradle全域性檔案gradle.properties 。在裡面定義一個變數isDebug= false 判斷是不是除錯模式。
然後去每個module的build.gradle中配置。
比如module_weather 中

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

manifest每次更改太麻煩,可以建一個debug資料夾指定一個manifest 比如:
這裡寫圖片描述

裡面指定module的入口activity。另外還得指定application的主題不然會報錯。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.chs.weather">

    <application
        android:name="com.chs.library.base.BaseApplication"
        android:allowBackup="true"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        >
        <activity android:name=".WeatherActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

然後build.gradle中指定manifest的路徑

    sourceSets {
        main {
            if (isDebug.toBoolean()) {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                java {
                    //release 時 debug 目錄下檔案不需要合併到主工程
                    exclude 'debug/**'
                    exclude '**/debug/**'
                }
            }
        }
    }

OK 終於配置完了。這個時候我們將isDebug改為ture,重新編譯後看到

這裡寫圖片描述

這時候我們選擇執行module_weather就可以當成一個新的app在手機上執行除錯了。
執行效果:

這裡寫圖片描述