【Android】變種:一份源碼根據(jù)需求打包不同APK

變種的使用環(huán)境

在開(kāi)發(fā)過(guò)程中我們可能會(huì)遇到這樣一種情況:
在現(xiàn)有的APP的基礎(chǔ)上更換部分UI或是增刪某個(gè)功能模塊坐漏,而大體上兩個(gè)apk有很多共通的地方薄疚,面對(duì)這種情況的解決方法無(wú)非以下幾種:

1.將現(xiàn)有代碼復(fù)制粘貼一份碧信,在另一個(gè)工程中進(jìn)行修改

2.使用代碼托管工具的分支功能

3.使用全局靜態(tài)變量控制某個(gè)功能開(kāi)關(guān)

以上方法在新打包的apk較少的情況下使用是沒(méi)什么問(wèn)題的,但當(dāng)需要打包的apk過(guò)多時(shí)街夭,以上方法在后續(xù)開(kāi)發(fā)時(shí)就會(huì)變得異常麻煩砰碴,所以面對(duì)這種情況板丽,變種便于管理的優(yōu)勢(shì)就體現(xiàn)得淋漓盡致

正式使用

gradle配置

首先需要在app所在的module的gradle中配置flavorDimensions(風(fēng)味維度)埃碱,使變種的flavors保持在同一維度中

flavorDimensions的值可以自由定義


配置flavorDimensions

然后需要在android下建立productFlavors,然后創(chuàng)建一個(gè)自定義名稱的子項(xiàng)
同時(shí)砚殿,在src下建立與main同級(jí)的文件夾啃憎,文件夾名稱需與在productFlavors中的子項(xiàng)名稱保持一致


變種配置

配置完成后點(diǎn)擊sync更新配置,使用變種的第一步就算完成了

此時(shí)我們可以點(diǎn)擊Android Studio的左下角的Build Variants標(biāo)簽頁(yè)展開(kāi)變種目錄辛萍,選擇對(duì)應(yīng)的變種耳幢,運(yùn)行時(shí)就會(huì)編譯對(duì)應(yīng)的變種中的文件

變種選擇

當(dāng)然,僅僅是這樣處理的話,我們打包出來(lái)的apk會(huì)互相覆蓋掉,這是因?yàn)榫幾g出來(lái)的apk包名一致導(dǎo)致的。

我們可以在productFlavors下對(duì)變種添加配置掠河,分別設(shè)置applicationId奉瘤,這樣肌访,編譯出來(lái)的apk就可以在手機(jī)上共存蟹演。

productFlavors{
        variant1{
            applicationId "com.sariki.variants1"
        }
        variant2{
            applicationId "com.sariki.variants2"
        }
    }

功能模塊配置

變種搭建完成后,那么就到了代碼環(huán)節(jié)羞反,需要注意的有兩點(diǎn):
1.變種中的文件繼承main目錄中的文件夾結(jié)構(gòu)布朦,所以新建變種目錄時(shí)需要將包結(jié)構(gòu)與main的包結(jié)構(gòu)保持一致


目錄結(jié)構(gòu)

2.變種與main中不能存在同級(jí)同名的文件,應(yīng)在main中刪除該文件后在變種目錄中創(chuàng)建
(例如:main目錄下activityA中需要跳轉(zhuǎn)到activityB昼窗,但這個(gè)activityB根據(jù)變種有不同的功能是趴,這時(shí)就需要在不同變種下創(chuàng)建該activityB并將main目錄下對(duì)應(yīng)的activityB文件刪除)


在這里插入圖片描述

既然打包多份app,我們?cè)谝恍┙缑婵赡芫蜁?huì)根據(jù)變種來(lái)對(duì)某些組件進(jìn)行顯示隱藏等操作澄惊,如果大體上與本體保持一致且沒(méi)有新增一些功能唆途,我們可以不采取刪除main目錄下文件后在變種中創(chuàng)建的方式來(lái)處理,可以借助BuildConfig類來(lái)進(jìn)行判斷掸驱。
首先我們到gradle下的productFlavors中對(duì)對(duì)應(yīng)的變種配置buildConfigField肛搬,
productFlavors{
        variant1{
            applicationId "com.sariki.variants1"
            buildConfigField("boolean", "SHOW_TOAST", "true")
        }
        variant2{
            applicationId "com.sariki.variants2"
            buildConfigField("boolean", "SHOW_TOAST", "false")
        }
    }

代碼中可以使用該變量來(lái)進(jìn)行判斷處理


在這里插入圖片描述

buildConfigField是在編譯時(shí)就會(huì)根據(jù)你的參數(shù)創(chuàng)建一個(gè)靜態(tài)成員變量,因此毕贼,我們也可以借由這個(gè)來(lái)進(jìn)行網(wǎng)絡(luò)端口配置來(lái)適應(yīng)變種對(duì)應(yīng)的服務(wù)器

productFlavors{
        variant1{
            applicationId "com.sariki.variants1"
            buildConfigField("boolean", "SHOW_TOAST", "true")
            buildConfigField("String", "SERVER_HOST", "\"http://111.111.11.1/\"")
        }
        variant2{
            applicationId "com.sariki.variants2"
            buildConfigField("String", "SHOW_TOAST", "true")
            buildConfigField("String", "SERVER_HOST", "\"http://222.222.22.2/\"")
        }
    }
在這里插入圖片描述

當(dāng)需要操作的變量過(guò)多時(shí)温赔,使用buildConfigField會(huì)使gradle文件變得過(guò)于冗雜,這個(gè)時(shí)候我們可以新建一個(gè)java文件作為一個(gè)載體帅刀,在其中創(chuàng)建靜態(tài)成員變量替代BuildConfig
(該文件也需要在不同變種文件夾的相同層級(jí)目錄中分別創(chuàng)建)


在這里插入圖片描述

在這里插入圖片描述

資源文件配置

與java文件相同让腹,資源文件如果根據(jù)變種有變更远剩,也需要在對(duì)應(yīng)的文件目錄中創(chuàng)建相同的文件,但資源文件與java文件有一個(gè)不同點(diǎn):變種同層級(jí)同名資源文件可與main目錄下的同層級(jí)同名資源文件共存骇窍,編譯時(shí)會(huì)選擇變種目錄下的文件瓜晤。

舉個(gè)栗子:
我們?cè)赼pplication中設(shè)置icon,以background_test文件為例腹纳,我們分別在main和變種的資源文件目錄下創(chuàng)建該文件
[圖片上傳失敗...(image-dd08fd-1606287792466)]
[圖片上傳失敗...(image-cfde7e-1606287792466)]
main目錄下的background_test文件為藍(lán)色痢掠;
variant1目錄下的background_test為灰色;
variant2目錄下的background_test為黑色嘲恍;
而最后編譯出來(lái)的apk顯示的為變種對(duì)應(yīng)background_test的顏色足画。
因此,在其他需要根據(jù)變種修改圖片資源文件的地方都可以按這種方式來(lái)修改佃牛。
[圖片上傳失敗...(image-dd8efd-1606287792466)]
說(shuō)完圖片文件淹辞,我們回到剛才所說(shuō)的編譯時(shí)會(huì)選擇變種文件目錄下的文件這一點(diǎn)。
在values文件夾下我們一般會(huì)使用strings.xml俘侠,colors.xml等文件來(lái)定義配置象缀,通常這些xml文件里面會(huì)有大量的item配置,如果根據(jù)上述圖片的操作去將xml文件完完整整的復(fù)制一份到變種目錄下就會(huì)造成資源浪費(fèi)以及擴(kuò)大工程占用空間爷速,但如果不復(fù)制這份xml文件又該怎么去修改文件里面的部分配置呢央星?
其實(shí)我們可以依靠xml配置的覆蓋機(jī)制來(lái)解決這個(gè)問(wèn)題。

舉個(gè)栗子:
我們?cè)趍ain目錄下的strings.xml中新建一行名為"test_txt"的string惫东,
編譯后需要在variant1中顯示為“變種1”莉给,variant2中顯示為“變種2”。
那么廉沮,我們可以直接在對(duì)應(yīng)變種目錄中新建一個(gè)xml文件(命名無(wú)強(qiáng)制規(guī)定)颓遏,然后在其中創(chuàng)建一個(gè)同樣名為"test_txt"的string并賦值。
這樣滞时,編譯后變種中xml的string會(huì)覆蓋掉main目錄下strings.xml中同名string的值州泊。

main目錄下strings.xml配置


在這里插入圖片描述

variant1目錄下variant_stings.xml配置


在這里插入圖片描述

variant2目錄下variant_stings.xml配置
在這里插入圖片描述

測(cè)試界面的xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:textSize="30sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:text="@string/tip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:textSize="30sp"
        android:text="@string/test_txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>

我們給textview設(shè)置內(nèi)容后可以編譯看一下效果

variants1:


在這里插入圖片描述

variant2:


在這里插入圖片描述

這樣,我們就可以達(dá)到只修改少量配置來(lái)更改文本或顏色的目的

密鑰配置

我們?cè)陂_(kāi)發(fā)過(guò)程中漂洋,可能會(huì)用到第三方的sdk來(lái)支持我們做一些功能,而這些sdk都會(huì)使用獨(dú)特的密鑰來(lái)獲得使用權(quán)限力喷,我們?cè)跇?gòu)建變種時(shí)這些sdk也需要用到刽漂,那么我們?cè)撊绾螌?duì)這些密鑰進(jìn)行配置呢?

現(xiàn)在大部分的sdk密鑰會(huì)需要在AndroidManifest中進(jìn)行配置弟孟,而其實(shí)在AndroidManifest中贝咙,我們可以直接引用gradle中的配置。

以網(wǎng)易云信密鑰配置為例:

<meta-data
            android:name="com.netease.nim.appKey"
            android:value="" />

我們需要在value一欄中填寫(xiě)從網(wǎng)易云信獲得的密鑰拂募,這時(shí)庭猩,我們可以使用引用的方式來(lái)進(jìn)行配置窟她。

首先我們到gradle下的productFlavors中,配置對(duì)應(yīng)變種的manifestPlaceholders參數(shù)

productFlavors{
        variant1{
            applicationId "com.sariki.variants1"
            manifestPlaceholders = [valueName : "xxxxxxxxxx"]
        }
        variant2{
            applicationId "com.sariki.variants2"
            manifestPlaceholders = [valueName : "xxxxxxxxxxxx"]
        }
    }

格式為 [自定義的變量名稱 : "密鑰值"]

然后回到清單文件中蔼水,使用${xxx}的方式來(lái)引用指定的值

<meta-data
            android:name="com.netease.nim.appKey"
            android:value="${valueName}" />

同樣震糖,也可以在部分需要完整包名結(jié)構(gòu)的配置中,使用${applicationId}方式來(lái)引用包名趴腋,例如部分廠商的推送以及一些其他的sdk配置吊说,例

<permission
        android:name="${applicationId}.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />

但也有部分無(wú)法直接使用引用的方式來(lái)使用包名的情況,如接入微信第三方登錄時(shí)用到的微信sdk优炬,需要我們手動(dòng)創(chuàng)建WXEntryActivity颁井,并在清單文件中聲明,

<activity android:name=".wxapi.WXEntryActivity"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.Translucent.NoTitleBar"
            android:exported="true"
            android:launchMode="singleTask"
            />

但由于我們構(gòu)建變種時(shí)使用的包名不同蠢护,name字段索引不到WXEntryActivity的位置雅宾,直接配置的話會(huì)出現(xiàn)WXEntryActivity無(wú)法被調(diào)起的情況礁哄,這時(shí)我們就需要使用別名來(lái)進(jìn)行配置

<activity-alias
            android:name="${applicationId}.wxapi.WXEntryActivity"
            android:exported="true"
            android:targetActivity=".wxapi.WXEntryActivity" />

添加這些配置就可以正常索引了

多module變種配置

在部分開(kāi)發(fā)環(huán)境中讽坏,我們可能會(huì)需要對(duì)多個(gè)module進(jìn)行變種配置赊颠,與上述配置流程沒(méi)有太大區(qū)別惧所,但是需要注意的有兩點(diǎn):
1.多個(gè)module的flavorDimensions需要保持一致
2.多個(gè)module的productFlavors需要保持一致羔挡,即module1中productFlavors中配置了多少個(gè)赶促,module2中同樣需要配置多少個(gè)
基于以上第二點(diǎn)妒蔚,建議不要在過(guò)多的module中進(jìn)行變種配置疮鲫,這樣會(huì)導(dǎo)致打一個(gè)新包需要配置的地方變得過(guò)多蘸劈,在配置環(huán)節(jié)變得繁瑣昏苏,有違初衷。

結(jié)尾

以上就是變種在構(gòu)建過(guò)程中會(huì)涉及的一些方法了威沫,但值得一提的是贤惯,變種有著馬甲包與渠道包區(qū)別,渠道包一般是為了統(tǒng)計(jì)上架不同應(yīng)用市場(chǎng)的流量而在APP中設(shè)置相關(guān)統(tǒng)計(jì)渠道棒掠,而馬甲包除了可以滿足渠道包的需求外孵构,可以針對(duì)不同的變種做出資源變更以及功能變更,更多使用于一份源碼根據(jù)需求定制化不同APP的情景烟很。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末颈墅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子雾袱,更是在濱河造成了極大的恐慌恤筛,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芹橡,死亡現(xiàn)場(chǎng)離奇詭異毒坛,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)煎殷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)屯伞,“玉大人,你說(shuō)我怎么就攤上這事豪直×右。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵顶伞,是天一觀的道長(zhǎng)饵撑。 經(jīng)常有香客問(wèn)我,道長(zhǎng)唆貌,這世上最難降的妖魔是什么滑潘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮锨咙,結(jié)果婚禮上语卤,老公的妹妹穿的比我還像新娘。我一直安慰自己酪刀,他們只是感情好粹舵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著骂倘,像睡著了一般眼滤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上历涝,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天诅需,我揣著相機(jī)與錄音,去河邊找鬼荧库。 笑死堰塌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的分衫。 我是一名探鬼主播场刑,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蚪战!你這毒婦竟也來(lái)了牵现?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤邀桑,失蹤者是張志新(化名)和其女友劉穎施籍,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體概漱,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年喜喂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瓤摧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竿裂。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖照弥,靈堂內(nèi)的尸體忽然破棺而出腻异,到底是詐尸還是另有隱情,我是刑警寧澤这揣,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布悔常,位于F島的核電站,受9級(jí)特大地震影響给赞,放射性物質(zhì)發(fā)生泄漏机打。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一片迅、第九天 我趴在偏房一處隱蔽的房頂上張望残邀。 院中可真熱鬧,春花似錦柑蛇、人聲如沸芥挣。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)空免。三九已至,卻和暖如春盆耽,著一層夾襖步出監(jiān)牢的瞬間蹋砚,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工征字, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留都弹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓匙姜,卻偏偏與公主長(zhǎng)得像畅厢,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子氮昧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容