Android 多渠道打包總結(jié)

為什么要多渠道

App一般會上傳多個(gè)應(yīng)用商店,例如應(yīng)用寶鸠珠、小米巍耗、華為秋麸、OPPO等渐排。

  1. 現(xiàn)在的手機(jī)都自帶應(yīng)用商店,用戶一般會在自帶應(yīng)用商店上搜索灸蟆。

  2. 產(chǎn)品驯耻、運(yùn)營可統(tǒng)計(jì)使用不同手機(jī)品牌的用戶的消費(fèi)情況,App日活炒考、月活可缚、UV、PV等斋枢。

傳統(tǒng)方式打包的痛點(diǎn)

如果App不是很出名帘靡,一般都需要自己打包上傳,除非像微信這種瓤帚,商店自動抓包進(jìn)行上傳描姚。曾經(jīng)參加過一個(gè)技術(shù)沙龍,微信的工程師說到戈次,他們的熱更新技術(shù)轩勘,如果使用了360加固進(jìn)行,就是失效怯邪,但是360應(yīng)用商店必須加固后才能上傳绊寻,所以他們就不上傳了,最后360應(yīng)用商店也還是抓包上傳了(可見有實(shí)力就是不一樣)。

不同的渠道澄步,可能App名字不同冰蘑,App圖標(biāo)Icon不同,如果手動替換的話村缸,體力勞動特別累人懂缕,筆者就曾經(jīng)做過一個(gè)app,需要給不同的景區(qū)生成app王凑,每個(gè)景區(qū)的圖標(biāo)和app名字都不一樣搪柑,傳統(tǒng)方式就是打包完一個(gè),手動替換再進(jìn)行打包索烹,10個(gè)還好工碾,慢慢后面景區(qū)越來越多,上升了50個(gè)百姓,工作量可想而知渊额,而且還容易錯(cuò),每個(gè)都需要手動檢查一遍(曾經(jīng)花2個(gè)小時(shí)垒拢,對著屏幕一個(gè)個(gè)替換旬迹,眼睛都花了,自然容易出錯(cuò)G罄唷)奔垦,這樣進(jìn)行幾次后,意識到這種重復(fù)勞動不應(yīng)該由我們來做尸疆,應(yīng)該交給機(jī)器呀椿猎!

Gradle配置多渠道打包

后面我們搜索了一下資料,決定使用Gradle配置的方式進(jìn)行打包寿弱,但他也有優(yōu)缺點(diǎn)犯眠。

  • 優(yōu)點(diǎn):就是每個(gè)包都只需要配置好要替換的文件和占位,即可開始打包症革。

  • 缺點(diǎn):每次打包一個(gè)渠道包筐咧,都需要編譯一次,項(xiàng)目非常大的時(shí)候噪矛,可謂非常耗時(shí)量蕊!

Manifest占位,動態(tài)替換meta標(biāo)簽值摩疑,動態(tài)標(biāo)識渠道

實(shí)現(xiàn)步驟

  1. 配置App模塊的build.gradle文件危融。

    • 使用productFlavors,添加2個(gè)渠道雷袋,例如360和應(yīng)用寶吉殃。
    • 注意如果是3.0版本的Gradle辞居,必須要添加上flavorDimensions緯度。
    • 使用manifestPlaceholders蛋勺,增加清單文件占位瓦灶,格式:[占位名:值, 占位名:值]。
apply plugin: 'com.android.application'

android {
    //省略其他配置...

    //3.0版本Gradle開始必須添加緯度
    flavorDimensions "default"
    
    //多渠道打包配置
    productFlavors {
        //渠道包
        app_360 {
            manifestPlaceholders = [channel: "app_360"]
        }
        app_qq {
            manifestPlaceholders = [channel: "app_qq"]
        }
    }
}
  1. 清單文件增加占位標(biāo)識
    • 例如我們使用友盟進(jìn)行渠道記錄抱完,增加一個(gè)meta_data標(biāo)簽贼陶,標(biāo)簽名為UMENG_CHANNEL,值是占位符標(biāo)識${channel}巧娱。

注意這個(gè)標(biāo)識要和第一步的build.gradle文件中的productFlavors配置一致

  • 例如build.gradle碉怔。
//占位名:channel,值:app_360
manifestPlaceholders = [app_name: "360渠道包", channel: "app_360"]
  • meta標(biāo)簽禁添。
<!-- 占位名:channel撮胧,不包括${} -->
<meta-data
    android:name="UMENG_CHANNEL"
    android:value="${channel}" />
  • 完整配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.zh.makechannelpackage">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <!-- 省略其他配置... -->

        <!-- 多渠道配置 -->
        <meta-data
            android:name="UMENG_CHANNEL"
            android:value="${channel}" />
    </application>
</manifest>
  1. 在Java代碼層讀取meta標(biāo)簽,獲取其值即可老翘。
//渠道工具類
public class ChannelUtil {
    /**
     * 獲取app包內(nèi)的渠道標(biāo)識
     */
    public static String getChannel(Context context) {
        try {
            PackageManager manager = context.getPackageManager();
            ApplicationInfo appInfo = manager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
            return appInfo.metaData.getString("UMENG_CHANNEL");
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return "";
        }
    }
}

//調(diào)用
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String channel = ChannelUtil.getChannel(getApplicationContext());
        Toast.makeText(getApplicationContext(), "當(dāng)前渠道:" + channel, Toast.LENGTH_SHORT).show();
    }
}

給不同的編譯環(huán)境配置清單Key

除了productFlavors中配置manifestPlaceholders生成渠道包外芹啥,在編譯環(huán)境buildTypes下也可以使用,例如不同編譯環(huán)境下铺峭,高德地圖的key不同墓怀,就可以在buildTypes中使用。

  1. 修改build.gradle配置卫键。
buildTypes {
    debug {
        manifestPlaceholders = [amap_key: "amp_debug_xxxxxkey"]
        //省略其他配置...
    }

    release {
        manifestPlaceholders = [amap_key: "amp_release_xxxxxkey"]
        //省略其他配置...
    }
}
  1. 清單文件中添加高德Key配置傀履。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.zh.makechannelpackage">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 配置高德key -->
        <meta-data
            android:name="com.amap.api.v2.apikey"
            android:value="${amap_key}" />
    </application>
</manifest>

Lib模塊共享,App接入問題

一般我們都會使用多個(gè)Lib進(jìn)行分模塊開發(fā)永罚,例如支付模塊的Pay庫中的微信回調(diào)Activity啤呼,7.0獲取文件需要使用FileProvider等,都需要制定包名呢袱,但是我們的lib需要提供給其他App進(jìn)行接入,清單文件的配置需要接入方配置翅敌,如果配置比較多羞福,而且后續(xù)版本需要更換配置,都需要接入方重新配置會比較麻煩蚯涮,那么可不可以將配置留在Lib庫中治专,通過動態(tài)配置的方式動態(tài)配置呢。

  1. 例如微信支付的回調(diào)Activity配置遭顶,通過applicationId占位张峰,可以動態(tài)獲取到接入方的包名,只要按照約定棒旗,在包名下建立wxapi包下喘批,建立WXEntryActivity文件即可。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.zh.makechannelpackage">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 動態(tài)配置微信回調(diào)Activity -->
        <activity
            android:name="${applicationId}.wxapi.WXEntryActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:exported="true"
            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
            
        <!-- 省略其他配置... -->
    </application>
</manifest>
  1. 再例如圖片選擇庫,使用到了FileProvider饶深,我們不希望接入方配置餐曹,則也可以使用applicationId進(jìn)行占位。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.zh.makechannelpackage">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- FileProvider配置 -->
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
        <!-- 省略其他配置... -->
    </application>
</manifest>

動態(tài)指定App名稱

經(jīng)過前面的配置已經(jīng)足夠配置出不同渠道標(biāo)識的渠道包了敌厘,我們還可以繼續(xù)拓展下台猴,例如不同渠道的App名字不同。(一般因?yàn)橛械膽?yīng)用商店會以App名稱而被拒...)

  1. 修改build.gradle文件俱两。

manifestPlaceholders中添加一個(gè)標(biāo)識饱狂,app_name,值為對應(yīng)的名稱宪彩。例如360渠道為360渠道包嗡官,應(yīng)用寶渠道為應(yīng)用寶渠道。

//3.0Gradle開始必須添加緯度
flavorDimensions "default"
//多渠道打包配置
productFlavors {
        //渠道包
        app_360 {
            manifestPlaceholders = [app_name: "360渠道包", channel: "app_360"]
        }
        app_qq {
            manifestPlaceholders = [app_name: "應(yīng)用寶渠道包", channel: "app_qq"]
        }
}

動態(tài)指定App包名

例如我們debug環(huán)境下毯焕,希望測試包和正式包能夠共存衍腥,那么共存就需要包名不同,所以可以對包名進(jìn)行替換或者加后綴纳猫。

  1. 修改build.gradle文件婆咸。
    • applicationIdSuffix,給包名添加后綴芜辕。例如測試版加上.internal后綴尚骄。
    • applicationId,整個(gè)替換包名侵续。(一般不會這么干倔丈,只是提一下)
//3.0Gradle開始必須添加緯度
flavorDimensions "default"
//多渠道打包配置
productFlavors {
    //開發(fā)版
    developer {
        //測試版加包名后綴,方便和正式版共存
        applicationIdSuffix ".internal"
        manifestPlaceholders = [app_name: "開發(fā)版", channel : "app_internal"]
    }
    production {
        //也可以完全替換包名
        applicationId "me.zh.demo"
        manifestPlaceholders = [app_name: "正式版",
                                channel: "app_internal"]
    }
}

動態(tài)生成變量

開發(fā)中状蜗,Log打印是必不可少的需五,但是我們在正式環(huán)境是需要將Log打印去掉的,常規(guī)做法就是打印函數(shù)前加一個(gè)LogEnable的變量轧坎,將打印調(diào)用去掉宏邮。而我們一般會在常量類中添加開關(guān)變量。

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Logger.setDelegate(new L());
        Logger.setLogPrintEnable(true);
    }
}
  • 問題:而每次打包之前都需要將開關(guān)變量設(shè)置為false缸血,很容易忘蜜氨。

  • 期望:我們希望不同buildType下的編譯環(huán)境,打印Log是不一樣的捎泻,例如buildType為debug時(shí)飒炎,Log開關(guān)為開,buildType為release時(shí)笆豁,Log開關(guān)為關(guān)郎汪。

  1. Gradle為我們提供了buildConfigField赤赊,用于動態(tài)生成變量在BuildConfig,那么我們給不同的buildType進(jìn)行添加即可怒竿。
//編譯類型
buildTypes {
    debug {
        //打印開關(guān)變量
        buildConfigField("boolean", "LOG_ENABLE", "true")
        //省略其他配置...
    }

    release {
        buildConfigField("boolean", "LOG_ENABLE", "false")
        //省略其他配置...
    }
}
  1. 在Java代碼中砍鸠,設(shè)置BuildConfig中生成的變量即可。
public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Logger.setDelegate(new L());
        Logger.setLogPrintEnable(BuildConfig.LOG_ENABLE);
    }
}

不同渠道替換資源文件

能替換包名和App名字了耕驰,原以為完畢爷辱,結(jié)果運(yùn)營說,360商店我們要出首發(fā)朦肘!圖標(biāo)和啟動圖要加上360的標(biāo)識饭弓!這時(shí)候就需要用到同結(jié)構(gòu)、同名文件夾來替換了媒抠。

  1. 在和main文件夾下弟断,建立同名渠道的文件夾,例如app_360渠道趴生,建立的文件夾名為app_360阀趴。

  2. assets文件夾、res文件夾苍匆、以及AndroidManifest.xml清單文件結(jié)構(gòu)都要和main中的一致刘急。

  3. AppIcon同名即可替換。清單文件同名即可浸踩,內(nèi)部特定不同的內(nèi)容即可叔汁。

多渠道配置1.png
多渠道配置2.png

關(guān)于Java代碼多渠道

上面說到資源文件,建立同目錄進(jìn)行替換检碗,那么Java代碼可以同級目錄下据块,同名Java文件替換嗎?很遺憾的是不可以折剃,需要不同渠道另假,進(jìn)行不同的邏輯時(shí),只能通過剛才ChannelUtil獲取渠道信息微驶,進(jìn)行邏輯判斷了浪谴。

總結(jié)

使用Gradle來進(jìn)行配置,大大減少了我們的工作量因苹,媽媽再也不用擔(dān)心我打渠道包需要2小時(shí)啦,至于gradle每構(gòu)建一個(gè)渠道包都編譯一次問題篇恒,可以單獨(dú)給assets設(shè)置一個(gè)配置文件扶檐,后續(xù)使用zip修改配置文件,達(dá)到只打一個(gè)包胁艰,其他包都使用打包工具進(jìn)行打包款筑,時(shí)間可以節(jié)省非常多智蝠。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奈梳,隨后出現(xiàn)的幾起案子杈湾,更是在濱河造成了極大的恐慌,老刑警劉巖攘须,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漆撞,死亡現(xiàn)場離奇詭異,居然都是意外死亡于宙,警方通過查閱死者的電腦和手機(jī)浮驳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捞魁,“玉大人至会,你說我怎么就攤上這事∑准螅” “怎么了奉件?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵,是天一觀的道長昆著。 經(jīng)常有香客問我县貌,道長,這世上最難降的妖魔是什么宣吱? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任窃这,我火速辦了婚禮,結(jié)果婚禮上征候,老公的妹妹穿的比我還像新娘杭攻。我一直安慰自己,他們只是感情好疤坝,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布兆解。 她就那樣靜靜地躺著,像睡著了一般跑揉。 火紅的嫁衣襯著肌膚如雪锅睛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天历谍,我揣著相機(jī)與錄音现拒,去河邊找鬼。 笑死望侈,一個(gè)胖子當(dāng)著我的面吹牛印蔬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脱衙,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼侥猬,長吁一口氣:“原來是場噩夢啊……” “哼例驹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起退唠,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤鹃锈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后瞧预,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屎债,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年松蒜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扔茅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,912評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡秸苗,死狀恐怖召娜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惊楼,我是刑警寧澤玖瘸,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站檀咙,受9級特大地震影響雅倒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弧可,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一蔑匣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧棕诵,春花似錦裁良、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至笛匙,卻和暖如春侨把,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妹孙。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工秋柄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蠢正。 一個(gè)月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓华匾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親机隙。 傳聞我的和親對象是個(gè)殘疾皇子蜘拉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評論 2 361