Gradle自動實現Android元件化模組構建

Tangpj發表於2018-08-03

Material Render Phone

不管全世界所有人怎麼說,我都認為自己的感受才是正確的。無論別人怎麼看,我絕不打亂自己的節奏。喜歡的事自然可以堅持,不喜歡的怎麼也長久不了。

—— 村上春樹

背景

隨著App的不斷迭代,業務會變得越來越複雜,業務模組會越來越多,且每個模組的程式碼也會變得越來越多。為了應對這一場景,我們需要把不同的業務模組劃分成一個個元件,在修改業務程式碼的時候只需要在對應模組修改就可以了。通過高內聚,低耦合的業務模組來保證工程的健壯性和穩定性。現在問題來了,當元件的數量變得越來多的時候,我們如何管理業務元件呢?

原創宣告: 該文章為原創文章,未經博主同意嚴禁轉載。 之前的文章是分享本人部落格的,現在在專欄重新發一次。

為什麼我們要用Gradle管理元件呢?

先來看看Android元件化需要實現的目標。(什麼是元件化構建?)

  1. 按照業務邏輯劃分模組
  2. 專案模組能夠單獨啟動測試
  3. 能夠根據需求引入或刪除某些業務模組
  4. 通過不同模組的組合,組成不同的App

對於第一點:需要根據技術架構和業務架構來劃分模組,這裡需要根據實際情況來考慮。我們需要優化的是第二、三、四點。

對於第二點:Android是通過應用com.android.application或com.android.library來決定該模組是以App模式還是以Library模式構建。App模式和Library模式的最大區別就是,App能夠啟動,而Library不可以。所以如果我們的模組能獨立啟動的話,我們需要每次手動去改動模組的build.gradle檔案。好一點的做法定義一個布林值來判斷是否處於debug模式,但是這裡有個問題是,不是每個模組都能獨立啟動的。所以無論採用何種方案,都需要我們手動管理。

對於第三點:當我們開發好業務模組後,可能我們需要頻繁的新增或刪除某些業務模組。如果是這樣的話,我們也是需要頻繁手動修改App的build.gradle。

對於第四點:有時候,我們可能會在不同的App中引用相同的元件(例如:滴滴的普通版和企業版,普通版包含企業版的功能),這個時候,我們也不希望要頻繁手動管理元件依賴,特別是在元件還可以獨立執行的時候。

所以,在我們實踐元件化的時候,最大的問題就是,我們需要頻繁的手動build.gradle檔案來管理元件應用的外掛和App的依賴。

使用Gradle來管理元件

先安利下筆者寫的Gradle外掛:Calces。如果覺得這個外掛有用的話,可以star下,如果你有更好的想法的話,可以向我提交pull request。

廢話少說,一下是通過Calces快速實現Android元件化構建的流程。

Demo地址:SimpleCalces

專案結構:

simple_calces_dir

  1. 引入依賴庫 在Gradle 2.1及更高版本的外掛構建指令碼程式碼: 在專案的build.gradle中

    buildscript {
        ...
    }
    plugins {
      id "calces.modules" version "1.0.11"
    }
    複製程式碼

    在較舊版本的Gradle中或需要動態配置的情況下的外掛構建指令碼程式碼:

       buildscript {
         repositories {
           maven {
             url "https://plugins.gradle.org/m2/"
           }
         }
         dependencies {
           classpath "gradle.plugin.com.tangpj.tools:calces:1.0.11"
         }
       }
       apply plugin: "calces.appConfig"
    複製程式碼
  2. 在專案build.gradle配置AppConfig

    appConfig {
        debugEnable false
    
        apps {
            app{
                modules ':library1', ':library2'
            }
        }
    
        modules{
            library1{
                mainActivity ".Library1Activity"
                applicationId "com.tangpj.library1"
                isRunAlone true
            }
            library2{
                mainActivity ".Library2Activity"
                applicationId "com.tangpj.library2"
                isRunAlone true
            }
        }
    
    }
    複製程式碼
  3. 在modules(子模組)引入模組自動化構建外掛 (注意:不需要手動配置com.android.library或com.android.application)

    apply plugin: 'calces.modules'
    複製程式碼

這樣我們就完成了元件化的構建了,是的,我們不再需要再手動管理單個元件了與App的構建了,通過Calces,我們能實現快速的元件化構建與多App同時構建。

那麼問題來了,我們如何實現元件間的通訊呢?在簡單的專案中,推薦該Demo一樣,通過使用Android隱式Intent來實現元件間通訊。在中大型專案中,筆者推薦使用阿里的路由解決方案:ARouter。具體使用方法參考官方文件就可以了,使用方法十分簡單。

**注意:**在使用隱式Intent實現元件件通訊的時候需要注意找不到相應元件異常:java.lang.IllegalStateException: Could not execute method of the activity。導致這個異常的原因是找不到目標元件導致的,所以在實際開發的時候,需要捕獲這一異常,並且根據專案實際情況來進行實際的處理。使用ARouter框架則能通過設定降級策略來實現異常處理(檢視ARouter文件瞭解更多)。

如果只是實現專案的簡單元件化,那麼看到這裡就可以了,如果希望實現更加靈活的元件化架構的讀者可以繼續看下去,下面筆者將全面分析元件化的優勢與筆者總結的元件化構建思想。

元件化構建簡述

元件化構建與其說是一種技術,不如說是一種思想。元件化構建是通過對專案重新劃分成一個個高內聚、低耦合的模組來解決專案過於臃腫,程式碼過於複雜的一種架構思想。

我們通過對Google官方的架構演示Demo todo-mvp進行拆分來對Android元件化進行深入的分析。

Demo地址:TodoCalces

todo系列app是Google android-architecture專案中為了演示Android架構的最佳實現而編寫的一系列演示Demo。todo app的特點是,它足夠簡單,程式碼量少,易於理解。但是又不會過於簡單,因為它是一個包含完成功能的App。它實現了任務列表、任務詳情、新建任務、編輯任務、統計任務的功能。

todo-mvp實現的功能:

  • 任務列表
  • 任務詳情
  • 新增/編輯任務
  • 統計任務

我們將以todo-mvp的功能來劃分為4個業務模組,將底層劃分為2個模組,分別是superLib(提供mvp架構支援與其它的一些支援庫功能)與dataLib(資料支援模組,Room提供底層資料庫支援功能)。對於大型專案還可以加入resLib支援模組,用來存放公共圖片資源、字元穿資源、樣式等。

架構劃分圖如下:

Todo Calces Architecture

從架構圖可以看出,所有的業務元件都依賴底層庫,而APP又依賴於業務元件,APP元件在這裡是作為一個獨立元件存在的。在一般的元件化實踐中,都不包含APP這個元件的,APP元件的存在是有其意義的。

首先,我們的元件化除了實現元件的獨立管理和動態配置APP所依賴的元件外,還有一個十分重要的目的就是,通過組合不同的元件,打包多個不同的APP。例如,QQ有分普通版和輕聊版,輕聊版是功能簡化版的QQ。如果我們使用元件化來管理工程的話,我們只需要把不需要的模組移除掉就可以了。而APP元件在這裡的作用是充當一個包裝盒,把需要的元件包裝進來。並且我們可以通過控制包裝盒的樣式來配置不同的APP風格。在這裡我們可以通過Application中的Style來實現。

這裡我們還是以todo-mvp為例,例如我們需要實現一個不包含統計功能的todo APP,按照我們的原理,我們只需要去掉statistics的依賴就可以了。

架構劃分圖如下:

Todo No Statistic Architectur

如果nostatsitcs需要不同的配色的方案的話,只需要在AndroidManifest的application標籤中配置對應的theme就可以了。

使用Calces實現todo-mvp的元件化

通過上面的分析,我們來試下對todo-mvp專案按照業務功能來劃分元件。我們先來看看劃分後的目錄:

todo_calces_dir

好了,我們已經對todo-mvp專案進行初步的劃分了。根據上面分析的理論得知,我們的業務模組是可以單獨執行的,並且我們能夠快速構建一個不包含statistics模組的APP。

我們只需要使用Calces就能快速實現我們需要的功能。

按照Calces的教程,我們得知,實現Calces只需要三個步驟:

  1. 引入依賴庫
  2. 在專案的build.gradle中配置AppConfig
  3. 在業務模組中引入模組自動化構c持續

第一點和第三點在其它所有專案中的配置都是一樣的,在這裡不作論述,下面我們看看對於TodoCalces專案,我們要如何配置AppConfig 。

appConfig {

    debugEnable false

    apps {
        app {
            mainActivity "com.tangpj.tasks.TasksActivity"
            modules ':modules:addtask',
                    ':modules:taskdetail',
                    ':modules:tasks',
                    ':modules:statistics'
        }

        app2 {
            name 'nostatistic'
            applicationId 'com.tangpj.nostatistic'
            modules ':modules:addtask',
                    ':modules:taskdetail',
                    ':modules:tasks'
        }

    }

    modules {
        addtask {
            name ":modules:addtask"
            applicationId "com.tangpj.addtask"
            mainActivity ".AddEditTaskActivity"
            isRunAlone false
        }

        taskdetail {
            name ":modules:taskdetail"
            applicationId "com.tangpj.taskdetail"
            mainActivity ".TaskDetailActivity"
            isRunAlone true
        }


        task {
            name ":modules:tasks"
            applicationId "com.tangpj.tasks"
            mainActivity ".TasksActivity"
            isRunAlone true
        }

        statistics {
            name ":modules:statistics"
            applicationId "com.tangpj.statistics"
            mainActivity ".StatisticsActivity"
            isRunAlone true
        }


    }
}
複製程式碼

根據AppConfig可以得出,我們分別配置了2個APP,分別是app1和app2。並且app2中是沒有依賴statistics的。現在我們兩個APP執行的對比圖。

app1(帶statistics模組):

todo_calces

app2(不帶statistics模組):

nostatistic

從執行圖可以看出,app1和app2的配色方案是不一樣的,並且app2中不帶statistics模組,通過對專案實行合理的劃分和引入Calces就能夠快速實現元件化構建了。

**結論:**通過Calces能輕鬆實現業務元件的管理,多APP的快速構建。當我們的業務元件只有4個的時候,可能無法體現Calces的優勢,但是如果我們的業務元件有40個的時候,Calces給我們帶來的優勢就非常明顯了。我們可以通過靈活依賴不同的元件,實現快速構建多個APP的目的。就像Calces的介紹圖案一樣,把元件當成積木來使用。

如何測試

Android自動化測試展開來說是一個非常大並且不算簡單的工程,在這裡筆者不打算展開來說。只是簡單的介紹元件化構建如何讓我們更方便地去測試。

並不是所有的自動化測試都一樣,它們通常在使用範圍、實現難度和執行時間上存在不同。我們一般把自動化測試劃分為三種分別是:

  1. 單元測試:目的是測試程式碼的最小單元。在基於Java的專案中,這個單元是一個方法。單元測試容易編寫,快速執行,並在開發過程中針對程式碼的正確性提供寶貴的反饋。
  2. 整合測試:用來測試一個完成的元件或子系統,確保多個類之間的互動是否按預期執行。整合測試需要比單元測試需要更長的執行時間,而且更加難以維護,失敗的原因難以診斷。
  3. 功能測試:通常用於測試應用程式端到端的功能,包括從使用者的角度與所有外部系統的互動。當我們討論使用者角度時,通常是指使用者介面。因為使用者介面會隨著時間的推移發生變動,維護功能測試程式碼會變得乏味而耗時。

為了優化投資回報率,程式碼庫應該包含大量的單元測試、少量整合測試以及更少的功能測試。

佔比如下圖所示:

自動化測試金字塔

從上文知道,在我們的元件化分的時候,會劃分一個基礎依賴庫(superLib)。基礎依賴庫為我們的專案提供了基本的支援,並且該庫在專案中是比較穩定、並且不包含業務邏輯的,所以在基礎依賴庫中,我們應該大量應用單元測試。而整合測試則適用於我們的資料依賴庫(dataLib)中,我們可以通過整合測試來驗證產品程式碼與資料模組的互動。而我們的業務模組中包含了大量的業務邏輯,這部分是經常變動的部分,我們可以為我們的業務模組編寫一些UI自動化測試程式碼,但是因為業務(介面)經常變動的原因,所以這部分測試程式碼是難以維護,並且複用性十分低的。。

最後,我們得出的結論是:應該把主要精力放在單元測試上,所以如果當你的精力不足以編寫所有測試程式碼的時候,你應該把主要的精力放在單元測試上,而不是放在收益最小的功能測試上。

關於自動化測試,筆者給的建議就到這裡了,如果需要深入理解測試的話,可以自行查詢資料,或者關注筆者的部落格。後續的部落格中,有可能會寫關於自動化測試相關的知識。

小結

通過Calces外掛,我們在實現Android元件化時只需要關注如何合理劃分元件的架構與如何實現元件間的通訊就可以了。對於Android元件化來說,最主要問題有兩個:

  1. 大型專案如何合理劃分元件模組
  2. 當專案的元件數量非常多的時候如何管理

第二個問題,可以通過Calces快速解決,至於第一個問題,筆者給出的指導就是,業務模組在合理的情況下要儘可能的小,因為越小的模組,越容易達到高內聚低耦合的目的。讀者不需要擔心專案模組劃分得過於細不便於管理的問題,因為Calces能夠輕鬆幫你管理好各個模組。

歷史精選

Android開發利器之Data Binding Compiler V2 —— 搭建Android MVVM完全體的基礎

關於我

如果這片文章對你有所啟發的話,可以關注下筆者的公眾號或GitHub

掃一掃關注我:

qr

相關文章