Android程序換膚架構

 

目錄

1、名詞解釋

2、 背景

2.1 換膚面臨的問題

2.2 換膚的目標

2.3 換膚的難點

3、實現方案

3.1 Res-Placeholder:資源的佔位符

3.2 skin

3.3 模塊

3.4 產品


1、名詞解釋

(1)skin:皮膚

應用程序主題,整體風格

(2)onlineRes:線上資源文件(onlineSkin對應的資源)

程序正在使用的皮膚使用的資源文件

(3)migrateRes:遷移的資源(migrateSkin對應的資源)

程序即將使用的新皮膚使用的資源文件

(4)色板:一套皮膚對應的基本色

2、 背景

2.1 換膚面臨的問題

在業務的發展過程中,App存在整體換膚的需求。如果我們對換膚和資源沒有實現良好的管理,則會導致應用程序使用大量不可管理的資源。從而造成以下三個問題:

  • 即使新皮膚完全替代了老皮膚,由於業務代碼裏面不規範使用了資源文件,onlineRes無法被清除
  • onLineRes和migrateRes混在一起,隨着換膚越來越多,造成資源文件膨脹無法管理
  • 業務邏輯裏面直接使用資源的引用,導致每次換膚需要修改大量的業務代碼(與換膚的定位不符合)

2.2 換膚的目標

  1. 支持不修改業務代碼換膚:不修改業務代碼,僅僅通過修改編譯時候指向資源文件的位置,實現換膚
  2. 支持逐步換膚:支持按頁面逐步換膚,逐步實現整個app的換膚(提前換膚的功能的上線時間)
  3. 支持老皮膚資源刪除:換膚之後,老的皮膚資源文件可完全刪除
  4. 支持皮膚升級和降級:換膚之後程序支持在新老皮膚之間切換,從而實現在皮膚的
  5. 支持不同的分支使用不同的皮膚:對於A分支使用皮膚A,B分支使用皮膚B,他們包含的資源文件可能不完全相同,但是分支A和分支B均能編譯通過

2.3 換膚的難點

(1)難點1:資源ID的管理

爲了實現【目標1:支持不修改業務代碼換膚】和【目標2:支持逐步換膚】,業務代碼裏面是要滿足一下功能:

  • 業務代碼裏面使用的資源文件在新皮膚和老皮膚裏面的的ID相同
  • 在逐步換膚的過程中onlineRes和migrateRes同時存在於項目之中
  • 對於相同的Res,業務代碼需要根據不同的情況使用onlineRes和migrateRes裏面的資源

(2)難點2:遷移邏輯的管理

爲了實現【目標2:支持逐步換膚】,【目標3:支持老皮膚資源刪】和【目標4:支持皮膚升級和降級】,業務代碼需要滿足以下功能:

  • 根據是否IF_MIGRATE的變量,切換到不同的UI實現的邏輯,並且在業務裏面裏面儘可能的減少IF_MIGRATE的使用

(3)難點3:不同flavor使用資源不同,但是編譯能通過

爲了實現【目標5:支持不同的分支使用不同的皮膚】,業務代碼需要保證:

  • 保證不同分支使用資源不完全相同的情況下,能夠編譯通過

3、實現方案

爲了滿足以上目標和解決以上的兩個難點,設計瞭如下的皮膚架構的組織方案。其由如下幾個部分組織起來

  • Res-Placehoder:資源佔位符
  • Skin:皮膚
  • 模塊:一個業務模塊
  • 產品:不同的產品由不同的模塊組織起來

遷移完成前架構:

遷移完成以後架構:

3.1 Res-Placeholder:資源的佔位符

(1)作用

保證不同flavor使用資源不一致也能編譯通過

(2)組成部分

  1. 可通過id覆蓋的資源:color,drawable,theme,等等。實現機制參見【3.2:色板】
  2. 不能通過id覆蓋的資源:font

font採用的是assets存儲的,Android在編譯的時候,不能同時存在兩個文件名相同的assets,因此不能才資源覆蓋的形式實現。

因此,在online-skin裏面添加了font1,那麼在migrate-skin裏面也要添加font1才能保證代碼在使用online-skin和migrate-skin的時候都能編譯通過

(3)資源賦值

均爲假值,而且明顯是假的,保證編譯之後立刻能發現

3.2 skin

每一套皮膚均有自己的實現,皮膚之間的相同定位的資源id相同,且實現對UIlib裏面資源佔位符的覆蓋。皮膚由以下幾個部分組成:

  • 色板:整套皮膚的主體色(不同皮膚的色板id均相同),詳情參見下文【色板】
  • 皮膚內,跨業務使用的資源:如圖片的佔位符

舉個例子:在DetailModule和FeedModule兩個業務場景下,對於migrateSkin,其使用的圖片的佔位符image_placeholder.drawable應該是相同的,因此其應該放在該migrateSkin的drawable文件下面

3.2.1 色板

一套皮膚裏面包含的主體色,每個顏色都有其對應的功能。比如說online-skin裏面包含的色板如下:

 

在migrate-skin裏面包含的色板如下:

其中,c0,c1,c2,c3代表的含義均相同,只是在不同的皮膚裏面取值不同。

因此在UIlib裏面,其需要有如下的color佔位符,用於保證其他的分支即使不使用這個資源依然能夠編譯通過。(eg:即使<online-skin,flavor1>在代碼裏面不使用b1的顏色,但是仍然需要在UIlib裏面添加b1,用戶保證<online-skin,flavor1>代碼能夠編譯通過)。在UIlib裏面添加資源佔位符的標準有以下兩個:

(1)公共色板包含的id(理論上所有公共色板包含的id均相同),此條件可以考慮弱化,後期可考慮刪除此條件

(2)該皮膚特有的資源,並且在代碼裏面直接有該資源id的引用

3.3 模塊

每一個具體的業務,如feed,detail等。每一個業務單獨的管理其資源和代碼文件,因此其有以下兩部分組成:code和res,

在遷移的過程中(<online-skin→migrate-skin>),該module使用的資源由以下三部分組成:

  • online res
  • migrate res
  • module res

在遷移完成之後(<migrate-skin>),該module使用的資源,由以下兩部分組成

  • migrate res
  • module res

切記不在module裏面放訪問其他module申明的資源和其他皮膚定義的資源(eg:在feed module使用online-skin的之後,只能訪問online-skin和feed module裏面的代碼)

3.3.1 模塊代碼實踐

爲了避免代碼中反覆出現if(migrate)的語句,我們將所有的資源管理在ResHelper裏面,有ResHelper負責資源的加載:

  • AbsResourceHepler:全局資源的加載
  • FeedResourceHelper:業務相關資源的加載
public class AbsResourceHepler {

    protected static int pickIconInDifferentStyle(int styleA, int styleB) {
        if (ApplicationVariables.useVenusStyle) {
            return styleB;
        }
        return styleA;
    }
}


public static int getImageCoverPlaceHolderRes() {
    return pickIconInDifferentStyle(R.drawable.simple_image_holder_listpage, R.color.C4_test);
}

3.4 產品

不同的模塊組裝起來就成了不同的產品,每一個產品由以下兩個維度進行定義<module,skin>

  • module:包含的模塊,所擁有的功能
  • skin:使用的皮膚,功能的展示樣子

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章