原文首發於微信公衆號:躬行之(jzman-blog),歡迎關注交流!java
當 App 項目複雜必定的程度,將項目組件化是必不可少的,組件化能夠更好的進行功能的劃分,提到組件化有人可能會想到模塊化,其實組件化和模塊化的本質是同樣的,都是爲了代碼重用的業務解耦,模塊化主要按照業務劃分,而組件化主要按照功能劃分,從組件化最基礎的幾個方面打開組件化的大門。android
組件化中兩個功能模塊時不直接依賴的,其依賴規則是經過 Base module 間接依賴,當組件之間的 Activity 進行界面跳轉時,因爲沒有相互依賴的關係,每每會沒法引用另外一個 module 中的 Activity。編程
隱式跳轉是經過 Android 原生 Intent 匹配機制來實現相應跳轉,就是使用 Action 來跳轉到對應的 Activity,這樣使用隱式跳轉的方式就能夠跨 module 實現 Activity 之間的跳轉了,注意一點,若是移出 Activity 所在的 module 而不移出相應的跳轉,若是繼續跳轉會出現異常,使用隱式 Intent 跳轉須要驗證是否會接收該 Intent,須要對該 Intent 對象調用 resolveActivity() 方法來判斷至少有一個應用可以處理該 Intent,經過隱式跳轉的方式還能夠設置 exported 爲 false 來確保只有本身的 App 纔可以啓動對應的組件。微信
在 Android 開發中可將 module 當作不一樣的網絡,而對應的 Router 就是鏈接各個 module 的中轉站,這個中轉站能夠對頁面跳轉的參數等進行統一處理,ARouter 是阿里開源出來的一個頁面跳轉路由,使用 ARouter 能夠替代隱式跳轉來完成不一樣 module、不一樣組件之間的跳轉以及跳轉過程的監聽、參數的傳遞等,ARouter 支持路徑跳轉和 URL 跳轉兩種方式,使用也很是靈活,ARouter 的具體使用這裏不作介紹,其具體使用會在單獨一篇文章中詳解,ARouter 與 Android 傳統跳轉方式的對好比下:網絡
組件化開發中最重要的一點就是各個模塊、各個組件之間要儘量解耦,這樣很容易就會想到使用 Java 中的反射機制,使用反射可在運行狀態下獲取某個類的全部信息,而後就能夠動態操做這個類的屬性和方法了。若是 Fragment 單獨做爲一個組件來使用時,當這個 Fragment 組件不須要被移出後,若是是常規的 Fragment 則會由於索引不到該 Fragment 而使得 App 崩潰,想一下若是使用反射建立 Fragment 的方式則至少不會引發 App 崩潰,這裏能夠捕捉異常完成相關邏輯,這樣是否是下降了耦合呢。可見,雖然反射有必定的性能問題,但使用反射確實能在必定程度上下降耦合,學習組件化 Java 反射機制應該是必須的一部分。app
組件化開發中要求每一個組件都能獨立運行,通常狀況下每一個組件都有必定的初始化步驟,最好的一種狀況是項目須要的幾個組件的初始化基本相同,那就可將初始化放在 BaseModule 中進行統一初始化,可是這種狀況畢竟比較理想,通常狀況是每一個組件的初始化都不同,可能你會想到在各自的 Application 初始化,若是在各自的 Application 中初始化,當在最終編譯因爲 Application 的合併不免會出一些問題,這種方式也不可取,到這裏又想到了反射,在各組件中建立初始化文件,而後在最終的 Application 中經過反射完成各個組件的初始化操做,這裏經過 Java 的反射機制完成了組件化開發中 Application 的動態配置。模塊化
組件化開發過程當中,若是 ModuleA 的 AmdroidManifest 文件中使用 android:name 屬性指定了相應的 Application,而主 App Module 的 AndroidManifest 文件中也使用 android:name 屬性指定了相對應的 Application,此時就必須在 主App Module 的 AndroidManifest 文件中使用 tools:replace="android:name" 來解決衝突,使用 replace 屬性表示該屬性也就是在 標籤下的 android:name 屬性可在編譯過程當中被替換,這樣根據 AndroidManifest 文件替換規則最終指定的 Application 應該是 App Module 中的指定的 Application。組件化
舉一個例子,我在項目中的某個功能 Module 中使用 SMSSDK 來完成短信驗證的功能,由於其餘地方不用,因此只引入到了要使用的功能 Module 中,若是其餘 Module 會使用應該將 SMSSDK 引入到 BaseModule 中,使用 SMSSDK 若是不指定該 Module 的 Application,MobSDK 會將 com.mob.MobApplication 指定爲該 Module 的 Application,此時在總體編譯打包時就會出現 AndroidManifest 文件的 android:name 屬性衝突,固然瞭解決方法就是使用 replace 屬性了。 AndroidManifest 文件合併後的主要衝突也就是這個問題了,固然 下的其餘屬性有衝突,也是使用 replace 屬性。在實際的開發中多驗證會更有收穫喔。性能
組件化開發中另外須要注意的一點是防止資源名稱同樣致使最終合併的時候,由於衝突形成資源引用錯誤或者某些資源丟失等,如字符串、顏色值等資源等合併的時候會被後面加載的相同名稱的資源所替換,解決的思路是在資源命名上要有必定的規則,能夠在 build.gradle 文件中配置 "resourcePrefix "組件名稱"" 的方式強制約束開發者確保資源名稱惟一,建議 Module 中資源的命名格式爲 "Module名稱功能其餘"。學習
組件化開發中,最終合併時每一個組件都是以 Lib Module 的形式存在,而 Lib Module 中 R.java 文件中定義的靜態變量沒有聲明爲 final,這就意味着不能在組件 Module 中使用相對應的常量了,如在時候 switch 語句就不能使用了,這就要求在組件中要使用 if 語句來替代 switch 語句,固然在組件獨立運行的時候是沒有這個問題的。
開發中常常會使用到 Butterknife,Butterknife 可很是方便的對 View 及 View 的事件等進行註解操做,它採用的是編譯時註解機制,註解中只能使用常量,因此在 Butterknife 在組件化開發中應該使用 R2 代替 R,R2 其實是 R 的拷貝, R2 對應聲明的變量是 final,因此在組件化開發中若是使用 Butterknife 在相應的註解中要使用 R2 替代 R。