banner
jzman

jzman

Coding、思考、自觉。
github

Android組件化基礎

PS: What you are currently insisting on may not have any effect at the moment, but it does not mean that it will not be useful at some point in the future.

當 App 專案變得複雜時,組件化是必不可少的。組件化可以更好地劃分功能,提到組件化,有人可能會想到模塊化,其實組件化和模塊化的本質是一樣的,都是為了代碼重用和業務解耦。模塊化主要按照業務劃分,而組件化主要按照功能劃分。從組件化的幾個基礎方面來打開組件化的大門。

  1. 組件之間的跳轉
  2. 動態創建
  3. 資源衝突
  4. 靜態常量

組件之間的跳轉#

在組件化中,兩個功能模塊之間不直接依賴,它們通過基礎模塊間接依賴。當組件之間的 Activity 進行界面跳轉時,由於它們沒有相互依賴的關係,往往無法引用另一個模塊中的 Activity。

隱式跳轉#

隱式跳轉是通過 Android 原生的 Intent 匹配機制來實現相應的跳轉。通過使用 Action 來跳轉到對應的 Activity,可以實現跨模塊的 Activity 跳轉。需要注意的是,如果將 Activity 移出所在的模塊而不移出相應的跳轉,如果繼續跳轉,將會出現異常。使用隱式 Intent 跳轉需要驗證是否會接收該 Intent,需要對該 Intent 對象調用 resolveActivity () 方法來判斷至少有一個應用能夠處理該 Intent。通過隱式跳轉的方式,還可以將 exported 設置為 false,以確保只有自己的 App 才能夠啟動相應的組件。

ARouter 跳轉#

在 Android 開發中,可以將模塊看作不同的網絡,而對應的 Router 就是連接各個模塊的中轉站。這個中轉站可以對頁面跳轉的參數等進行統一處理。ARouter 是阿里開源的一個頁面跳轉路由,使用 ARouter 可以替代隱式跳轉,實現不同模塊、不同組件之間的跳轉以及跳轉過程的監聽、參數傳遞等。ARouter 支持路徑跳轉和 URL 跳轉兩種方式,使用也非常靈活。ARouter 的具體使用這裡不做介紹,將在單獨的一篇文章中詳解。ARouter 與 Android 傳統跳轉方式的對比如下:

  1. 顯示跳轉需要依賴於類,而路由跳轉通過指定的路徑跳轉;
  2. 隱式跳轉通過 AndroidManifest 集中管理,導致協作開發困難;
  3. 原生使用 AndroidManifest 來註冊,而路由使用註解註冊;
  4. 原生 startActivity 之後跳轉過程交由 Android 系統控制,而路由跳轉採用的是 AOP 切面編程,可以對跳轉過程進行攔截和過濾。

動態創建#

組件化開發中最重要的一點是各個模塊、組件之間要盡可能解耦。這時很容易想到使用 Java 中的反射機制。使用反射可以在運行時獲取某個類的所有信息,然後可以動態操作這個類的屬性和方法。如果 Fragment 單獨作為一個組件使用時,當這個 Fragment 組件不需要被移出時,如果是常規的 Fragment,則會因為找不到該 Fragment 而導致 App 崩潰。但是如果使用反射創建 Fragment 的方式,至少不會引起 App 崩潰。在這種情況下,可以捕獲異常並完成相關邏輯,這樣是否降低了耦合呢?可以看出,雖然反射有一定的性能問題,但使用反射確實可以在一定程度上降低耦合。學習組件化的 Java 反射機制應該是必須的一部分。

組件化開發要求每個組件都能獨立運行。一般情況下,每個組件都有一定的初始化步驟。最好的情況是,項目需要的幾個組件的初始化基本相同,這樣可以將初始化放在 BaseModule 中進行統一初始化。但這種情況比較理想,一般情況下,每個組件的初始化都不一樣。你可能會想到在各自的 Application 中進行初始化。如果在各自的 Application 中進行初始化,當在最終編譯時由於 Application 的合併,可能會出現一些問題,這種方式也不可取。在這裡又想到了反射。在各個組件中創建初始化文件,然後在最終的 Application 中通過反射完成各個組件的初始化操作。通過 Java 的反射機制,完成了組件化開發中 Application 的動態配置。

資源衝突#

在組件化開發過程中,如果 ModuleA 的 AndroidManifest 文件中使用 android 屬性指定了相應的 Application,而主 App Module 的 AndroidManifest 文件中也使用 android 屬性指定了相對應的 Application,此時就必須在主 App Module 的 AndroidManifest 文件中使用 tools="android" 來解決衝突。使用 replace 屬性表示該屬性,也就是在 標籤下的 android 屬性,可以在編譯過程中被替換。根據 AndroidManifest 文件替換規則,最終指定的 Application 應該是 App Module 中指定的 Application。

舉個例子,我在專案中的某個功能 Module 中使用 SMSSDK 來完成短信驗證的功能。因為其他地方不使用,所以只引入到了要使用的功能 Module 中。如果其他 Module 會使用,應該將 SMSSDK 引入到 BaseModule 中。如果不指定該 Module 的 Application,MobSDK 會將 com.mob.MobApplication 指定為該 Module 的 Application。此時,在整體編譯打包時,就會出現 AndroidManifest 文件的 android 屬性衝突。當然,解決方法就是使用 replace 屬性。AndroidManifest 文件合併後的主要衝突就是這個問題。當然, 下的其他屬性也會有衝突,也可以使用 replace 屬性。在實際開發中,多驗證會更有收獲。

組件化開發中另外需要注意的一點是防止資源名稱一樣導致最終合併時,因為衝突造成資源引用錯誤或者某些資源丟失等問題。例如,字符串、顏色值等資源在合併時會被後面加載的相同名稱的資源所替換。解決的思路是在資源命名上要有一定的規則,可以在 build.gradle 文件中配置 "resourcePrefix" 組件名稱 ""的方式強制約束開發者確保資源名稱唯一。建議模塊中資源的命名格式為"Module 名稱_功能_其他 "。

靜態常量#

在組件化開發中,最終合併時每個組件都是以 Lib Module 的形式存在。而 Lib Module 中 R.java 文件中定義的靜態變量沒有聲明為 final,這就意味著不能在組件 Module 中使用相對應的常量。例如,在使用 switch 語句時就不能使用了。這就要求在組件中要使用 if 語句來替代 switch 語句。當然,在組件獨立運行時是沒有這個問題的。

在開發中經常會使用到 Butterknife。Butterknife 可以非常方便地對 View 及 View 的事件等進行註解操作。它採用的是編譯時註解機制,註解中只能使用常量。所以在組件化開發中,應該使用 R2 代替 R。R2 實際上是 R 的拷貝,R2 對應聲明的變量是 final。所以在組件化開發中,如果使用 Butterknife,在相應的註解中要使用 R2 替代 R。下一篇將介紹組件化中的 Application。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。