元件化實踐詳解(二)

頭條祁同偉發表於2018-04-13

在上一篇文章《元件化實踐詳解(一)》中我們介紹了元件化實踐的目標和實踐步驟,本文繼續說說關於元件化實踐遇到的問題及思考。

本文概述

1、元件內的架構設計

這條本來我是不想寫的,但是很多元件化的文章裡都會費盡心思的寫元件內的架構設計。

那我也談一談我的看法:首先回歸初心,想想元件化的目的,為了各個業務元件可以單獨執行。劃重點:目的是單獨執行,把之前在App Module的程式碼挪到自己單獨的Module,然後能夠獨立執行;而不是大面積重構!!我也相信對於大部分團隊,實際上並沒有很多的時間去做重構,尤其是在做元件化的過程中同時大面積重構,確定做了風險評估嗎

對於元件化的整體設計,需要遵循制定的規則,但是對於元件內的架構設計,實際上不需要特殊的要求,程式碼你愛怎麼寫就可以怎麼寫,不管你使用MVC、MVP還是MVVM,根據各自情況合理選擇就好了。這個話題本來就不屬於元件化專案的範疇。

元件化實踐詳解(二)

2、除錯方式

假如之前的幾步我們都順利完成,現在整個Project已經變成了下圖整個樣子。

元件化實踐詳解(二)

那我們的Module要怎麼才能跑起來呢?

  1. 我們建立Module的時候如果選擇的是Application工程,毋庸置疑肯定是能跑起來的,但是卻沒有辦法被真正的Host宿主引用了
  2. 建立Module的時候選擇的是Library工程,可以被引用但是無法自己單獨執行

二者結合呢?藉助馮大大在MDCC上的分享,將Module分為兩個模式:Release模式和Debug模式,Release模式下作為Library,供宿主依賴;而在Debug模式下則作為Application工程,自己單獨執行

build.gradle中根據gradle中的一個屬性值來判斷處於哪種模式下:

    if (isDebug.toBoolean()) {
        apply plugin: 'com.android.application'
    } else {
        apply plugin: 'com.android.library'
    }
    
     sourceSets {
        main {
            if (isDebug.toBoolean()) {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/release/AndroidManifest.xml'
            }
        }
    }   
複製程式碼

元件化實踐詳解(二)

在Release中的AndroidManifest配置預設啟動的主Activity。

這樣一來除錯的問題也就解決了。但是實際上這僅僅是一個Demo雛形,我們思考幾個帶出來的問題:

  1. 如果我這個業務元件涉及了別的好幾個Module,也就是我自己使用Debug模式執行的時候別的Module需要使用Release模式,那麼每個Module都需要一個單獨的變數去控制,Module過多導致變數過多,出錯可能性陡增
  2. 直接的原始碼依賴:假如大家在一個分支上開發不同的元件,有極大可能會導致衝突或者呼叫異常導致影響研發效率;當然肯定有人會說一般不會在一個分支開發,確實,但做了元件化之後分支數其實是可以變少的
  3. 如果是Host直接原始碼依賴子Module,那當子Module作為Application工程執行的時候一旦你Build專案,那各種美如畫的報錯讓你苦不堪言

如何對這個情況做優化:各個獨立Module提供穩定版本的aar

  • 單獨的業務元件開發完成之後,記錄一個版本號同時提供一個穩定的aar
  • 別的依賴模組直接去compile需要的業務元件的aar即可
  • Host宿主則是compile這些業務元件穩定的aar
  • 依賴於穩定的aar,那麼自己的業務元件開發並不會影響到別人,而且Build的時候宿主也不會出錯
  • 針對眾多的aar包,最好建一個maven倉庫來統一管理
  • 生成及上傳aar包這步,可以寫個指令碼幫忙生成及上傳

元件化實踐詳解(二)

3、工程化經驗

以下介紹些關於工程化的經驗

3.1 Application

部分業務元件一定會遇到依賴一些三方元件需要提前初始化的情況,正常我們的做法都是在應用的Application中做的。此時我們在獨立的Module開發,沒有了應用的Application,那麼可以自己建立一個Module的Application,以下提幾種實現的思考

  1. Module的Application只工作在Debug模式下,而在宿主中也同樣註冊需要的三方元件,Release模式下沒有這個Application則不會重複註冊

元件化實踐詳解(二)

  1. Module的Application同時工作於這兩種模式下,但是真正打包生成Apk之後實際系統認可的只有App的Application,而別的Module Application只是被系統認為是一個沒有特殊意義的普通類。那我們可以在真正的Application方法呼叫的時候通過反射呼叫Module Application的相應方法

元件化實踐詳解(二)

  1. 源於第二種方案,區別在於將Module元件中的初始化工作,延遲到元件使用的時候才去初始化,好處就是使用時才載入

元件化實踐詳解(二)

備註:而怎麼判斷呼叫業務Module呢?兩個場景:UI跳轉或者方法呼叫,這兩種判斷可以使用路由框架來協助。

3.2 Application與Tinker的相容

Tinker作為熱修復的可靠解決方案,想必很多App都會整合,但是Tinker整合稍繁瑣的地方就在於:為了確保Application也能修復,需要改造Application,改造完成之後打的包出來真正的Application已經被修改,而寫上了我們邏輯的Application實際上變成了一個普通類,只是相應方法被真正的Application呼叫

那我想把Tinker這個模組也單獨作為一個Module來使用,能行嗎?首先來思考一個問題:Application要在哪裡,Library中還是Host

答案是Library中,因為各個Module中可能會存在不方便獲取Context的場景,解決方案之一就是使用Application的Context。假設Application放在Host中,那Module肯定是無法使用的。當然剛剛我們說到Module自己的Application,但是別忘了如果Module沒有呢?

把Application放到Library中也不是說移就能移:

  • 正常情況下我們會發現Host Application中也有一大堆的邏輯或者是元件初始化,而這些元件要全被移出去還需要 a long long time
  • 程式碼中有一大堆通過Host Application拿到的Context,現在忽然移出去,必定一大堆報錯

面對如此抉擇,那到底是移還是不移?一個好方法是原來Host的Application只做較小改動:並不移出來之前的各種邏輯和元件,而是作為一個普通類,在Library中的Application方法執行時去回撥相應Host Application的方法;隨後在元件化的過程中逐漸的移出來這些業務和元件。這樣的改動成本最小又滿足了當下的需要

元件化實踐詳解(二)

3.3 資源衝突

在拆分出來多個Module或者新建Module進行開發,新建資源的時候可能會有命名的衝突,對Gradle熟悉的同學可能會表示使用resourcePrefix來進行限定,但是坦白說效果一般,倒是不如在編碼規範中加上一條以相應Module的標示作為命名的字首

3.4 ButterKnife的使用

ButterKnife——相信很多同學都用過,這是一個註解框架,一般在繫結View的時候使用,減少了很多無意義的程式碼。在正常開發中我們用起來也是6的飛起!然而當ButterKnife跑在Library工程中的時候各種Build失敗就出現了:原因在於Android Library中的R檔案欄位並不是常量,Module在Debug模式下是Application工程可以開心玩耍,等真正整合的時候切換回Release模式就呵呵噠了

在ButterKnife8.0之後也支援了在Library中使用,解決方式就是同時生成了一個R2,這個就是常量,因而可以在Library工程中使用。

推薦使用Android ButterKnife Plugin Plus外掛,方便的一鍵生成然後將R更改為R2;或者自己仿照去寫一個AS外掛,直接生成R2

備註:同時注意R2只能使用在註解中,因而點選事件要寫成這樣:

    @OnClick(R2.id.tv_back_selerole)
    public void onClick(View view) {
        if (view.getId() == R.id.tv_back_selerole) {
            dealBack();
        }
    }
複製程式碼

元件化實踐詳解(二)

3.5 避免過大的基礎庫

這個問題的引出是在元件化相對成熟的階段,已經初步完成了我們的預期目標,但是細化的過程中逐漸意識到一個問題:Library庫越來越大,其實單獨的一個Module並不會需要那麼多的元件,但是單獨Module執行的時候還是被引用上了,也會拖慢單獨Module的執行速度

於是我們提出了另外一個名詞:去中心化。將基礎庫進行細粒度的拆分,將開發中一定會用到的例如網路請求、EventBus、公共類等放在了Library中,而將別的不常用三方元件如地圖等移出去,只供需要的Module去依賴,而普通的Module則只依賴常用的Library。

這樣可以有效的避免Library逐漸變得膨脹,也給各Module只去依賴自己需要的特定Library能力

4、元件化成效

歷經千辛萬苦我們對專案做了元件化實踐,那究竟收穫了哪些好處呢?

  • 程式碼結構層次清晰明瞭;
  • 元件間界限清晰、有明確邊界,低耦合;
  • 開發過程體驗好,快速編譯;
  • 版本週期內沒有動到的元件快速回歸;
  • 方便A/BTest;

廣告時間

今日頭條各Android客戶端團隊招人火爆進行中,各個級別和應屆實習生都需要,業務增長快、日活高、挑戰大、待遇給力,各位大佬走過路過千萬不要錯過!

本科以上學歷、非頻繁跳槽(如兩年兩跳),歡迎加我的微信詳聊:KOBE8242011

歡迎關注

相關文章