1. 程式人生 > >android中多渠道打包的三種方式

android中多渠道打包的三種方式

國內的Android開發者還是很苦逼的,由於眾所周知的原因,google play無法在國內開啟(翻牆的就不在考慮之內了),所以Android系的應用市場,群雄爭霸。後果就是國記憶體在著有眾多的應用市場,產品在不同的渠道可能有這不同的統計需求,為此Android開發人員需要為每個應用市場釋出一個安裝包,這裡就引出了Android的多渠道打包。

首先我們說明一下什麼是多渠道打包?

國記憶體在著眾多的Android應用市場,為了統計不同安卓應用市場的下載量一個個性化統計需求,需要為每個應用市場的Android包設定一個可以區分應用市場的標識,這個為Android包設定應用市場標識的過程就是多渠道打包。

幾種主流的多渠道打包方式,以及其優劣勢

  • 通過配置gradle指令碼實現多渠道打包

這種打包方式是使用Android Studio的編譯工具gradle配合使用的,其核心原理就是通過指令碼修改AndroidManifest.xml中的mate-date內容,執行N次打包簽名操作實現多渠道打包的需求,具體實現如下。

(一)在Androidmanifest.xml中定義mate-data標籤

<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"    
    package="your.package.name"
>
<application> <meta-data Android:name="UMENG_CHANNEL" Android:value="{UMENG}"/> </application> </manifest>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

這裡需要注意的是:上面的value的值要和渠道名所對應,比如wandoujia裡面要對應為你豌豆莢的渠道名稱

(二)在build.gradle下的productFlavors定義渠道號:

productFlavors {  

        internal {}  

        /*InHouse
{} pcguanwang {} h5guanwang {} hiapk {} m91 {} appchina {} baidu {} qq {} jifeng {} anzhi {} mumayi {} m360 {} youyi {} wandoujia {} xiaomi {} sougou {} leshangdian {} huawei {} uc {} oppo {} flyme {} jinli {} letv {}*/ productFlavors.all { flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

同時需要注意的是,這裡需要在defaultConfig中配置一個預設的渠道名稱

manifestPlaceholders = [UMENG_CHANNEL_VALUE: "channel_name"]  
  • 1
  • 1

實現多渠道打包更換mate-data標籤中的內容

優勢:方便靈活,可以根據自身的需求配置不同的渠道執行不同的邏輯; 
劣勢:打包速度過慢;

  • 使用第三方打包工具

這種方式就是使用第三方的服務,比如360,百度,友盟等,其原理也是通過修改AndroidManifest.xml中的mate-data標籤內容,然後執行N次打包簽名的操作實現多渠道打包的。這裡就不在做具體解釋說明,免得又做廣告的嫌疑,O(∩_∩)O哈哈~。

優勢:簡單方便,幾乎不用自身做什麼工作; 
劣勢:打包速度過慢;

  • 使用美團多渠道打包方式

而這裡主要是根據美團客戶端打包經驗(詳見:美團Android自動化之旅—生成渠道包) 
主要是介紹利用在META-INF目錄內新增空檔案的方式,實現批量快速打包Android應用。

實現原理

Android應用安裝包apk檔案其實是一個壓縮檔案,可以將字尾修改為zip直接解壓。解壓安裝檔案後會發現在根目錄有一個META-INF目錄。如果在META-INF目錄內新增空檔案,可以不用重新簽名應用。因此,通過為不同渠道的應用新增不同的空檔案,可以唯一標識一個渠道。 
“採用這種方式,每打一個渠道包只需複製一個apk,在META-INF中新增一個使用渠道號命名的空檔案即可。這種打包方式速度非常快,900多個渠道不到一分鐘就能打完。”

實現步驟

(一)編寫渠道號檔案

(二)編寫Python指令碼,實現解壓縮apk檔案,為META-INF目錄新增檔案,重新壓縮apk檔案等邏輯:

# coding=utf-8
import zipfile
import shutil
import os

def delete_file_folder(src):  
    '''delete files and folders''' 
    if os.path.isfile(src):  
        try:  
            os.remove(src)  
        except:  
            pass 
    elif os.path.isdir(src):  
        for item in os.listdir(src):  
            itemsrc=os.path.join(src,item)  
            delete_file_folder(itemsrc)  
        try:  
            os.rmdir(src)  
        except:  
            pass

# 建立一個空檔案,此檔案作為apk包中的空檔案
src_empty_file = 'info/empty.txt'
f = open(src_empty_file,'w')
f.close()

# 在渠道號配置檔案中,獲取指定的渠道號
channelFile = open('./info/channel.txt','r')
channels = channelFile.readlines()
channelFile.close()
print('-'*20,'all channels','-'*20)
print(channels)
print('-'*50)

# 獲取當前目錄下所有的apk檔案
src_apks = [];
for file in os.listdir('.'):
    if os.path.isfile(file):
        extension = os.path.splitext(file)[1][1:]
        if extension in 'apk':
            src_apks.append(file)

# 遍歷所以的apk檔案,向其壓縮檔案中新增渠道號檔案
for src_apk in src_apks:
    src_apk_file_name = os.path.basename(src_apk)
    print('current apk name:',src_apk_file_name)
    temp_list = os.path.splitext(src_apk_file_name)
    src_apk_name = temp_list[0]
    src_apk_extension = temp_list[1]

    apk_names = src_apk_name.split('-');

    output_dir = 'outputDir'+'/'
    if os.path.exists(output_dir):
        delete_file_folder(output_dir)
    if not os.path.exists(output_dir):
        os.mkdir(output_dir)

    # 遍歷從檔案中獲得的所以渠道號,將其寫入APK包中
    for line in channels:
        target_channel = line.strip()
        target_apk = output_dir + apk_names[0] + "-" + target_channel+"-"+apk_names[2] + src_apk_extension
        shutil.copy(src_apk,  target_apk)
        zipped = zipfile.ZipFile(target_apk, 'a', zipfile.ZIP_DEFLATED)
        empty_channel_file = "META-INF/uuchannel_{channel}".format(channel = target_channel)
        zipped.write(src_empty_file, empty_channel_file)
        zipped.close()

print('-'*50)
print('repackaging is over ,total package: ',len(channels))
input('\npackage over...')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

(三)打包一個正常的apk包 
(四)執行python指令碼,多渠道打包 
(五)Android程式碼中獲取渠道號

/**
 * 渠道號工具類:解析壓縮包,從中獲取渠道號
 */
public class ChannelUtil {
    private static final String CHANNEL_KEY = "uuchannel";
    private static final String DEFAULT_CHANNEL = "internal";
    private static String mChannel;

    public static String getChannel(Context context) {
        return getChannel(context, DEFAULT_CHANNEL);
    }

    public static String getChannel(Context context, String defaultChannel) {
        if (!TextUtils.isEmpty(mChannel)) {
            return mChannel;
        }
        //從apk中獲取
        mChannel = getChannelFromApk(context, CHANNEL_KEY);
        if (!TextUtils.isEmpty(mChannel)) {
            return mChannel;
        }
        //全部獲取失敗
        return defaultChannel;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
    /**
     * 從apk中獲取版本資訊
     *
     * @param context
     * @param channelKey
     * @return
     */
    private static String getChannelFromApk(Context context, String channelKey) {
        long startTime = System.currentTimeMillis();
        //從apk包中獲取
        ApplicationInfo appinfo = context.getApplicationInfo();
        String sourceDir = appinfo.sourceDir;
        //預設放在meta-inf/裡, 所以需要再拼接一下
        String key = "META-INF/" + channelKey;
        String ret = "";
        ZipFile zipfile = null;
        try {
            zipfile = new ZipFile(sourceDir);
            Enumeration<?> entries = zipfile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = ((ZipEntry) entries.nextElement());
                String entryName = entry.getName();
                if (entryName.startsWith(key)) {
                    ret = entryName;
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (zipfile != null) {
                try {
                    zipfile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        String channel = "";
        if (!TextUtils.isEmpty(ret)) {
            String[] split = ret.split("_");
            if (split != null && split.length >= 2) {
                channel = ret.substring(split[0].length() + 1);
            }
            System.out.println("-----------------------------");
            System.out.println("渠道號:" + channel + ",解壓獲取渠道號耗時:" + (System.currentTimeMillis() - startTime) + "ms");
            System.out.println("-----------------------------");
        } else {
            System.out.println("未解析到相應的渠道號,使用預設內部渠道號");
            channel = DEFAULT_CHANNEL;
        }
        return channel;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

整個打包的流程就是這樣了,打工工具可參考github上的專案:多渠道打包實現

優勢:打包速度很快,很方便; 
劣勢:不夠靈活,不能靈活的配置不同的渠道不同的業務邏輯;

問題: 
專案中由於使用了友盟統計,以前是在meta-data中儲存渠道資訊,現在更改了方式之後需要手動執行渠道號的設定程式碼:

String channel = ChannelUtil.getChannel(mContext);
        System.out.println("啟動頁獲取到的渠道號為:" + channel);
        // 設定友盟統計的渠道號,原來是在Manifest檔案中設定的meta-data,現在啟動頁中設定
        AnalyticsConfig.setChannel(channel);
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

通過這種打包方式以前需要一個小時的打包工作現在只需要一分鐘即可,極大的提高了效率,目前在實際的應用中尚未發現有什麼問題,有這種需求的童鞋可以嘗試一下。

總結

雖說我們總結了三種打包方式,但是其實通過gradle打包和使用第三方服務打包都是執行了N次的打包簽名操作,時間上耗費太多,因此不太推薦,而美團的方式在效率上提高了很多,但是對於那種不同的渠道包執行不同的業務邏輯的需求就無能為例了,只能通過gradle配置,因此大家在選擇多渠道打包方式的時候可以根據自身的需求來選擇。

相關推薦

android多渠道打包方式

國內的Android開發者還是很苦逼的,由於眾所周知的原因,google play無法在國內開啟(翻牆的就不在考慮之內了),所以Android系的應用市場,群雄爭霸。後果就是國記憶體在著有眾多的應用市場,產品在不同的渠道可能有這不同的統計需求,為此Android開發人員

Android多媒體學習十二:AndroidVideo的播放方式的實現

在Android中,我們有三種方式來實現視訊的播放。 1、使用其自帶的播放器。指定Action為ACTION_VIEW,Data為Uri,Type為其MIME型別。 2、使用VideoView來播放。在佈局檔案中使用VideoView結合MediaController來實

Android多渠道打包方式(帶秒速打包

多渠道打包,主要是為了統計不同的渠道上包的下載數量,統計可以用umeng統計或者mta統計,本文用的是mta統計。 其實多渠道打包的實現與統計是沒有關係的,多渠道打包與統計之間的關係是我們要把String型別的渠道號交給統計。例如mta統計裡

Struts2學習(二)運行Action方法的方式

tracking 利用 content con return -m i++ var itl 1.運行execute()方法 一般的能夠直接在action中書寫execute,調用action時會自己主動運行此方法 2.配置method方法 在s

android解析XML的方式 DOM、SAX、PULL

第一種DOM是全部解析出來,消耗記憶體空間 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android圖片壓縮的方式

為了避免oom的出現,幾乎每個應用都會對大圖進行壓縮,我現在手頭做的產品就有很多地方用到,以前封裝工具類的時候,都是在網上找東找西,然後拼拼湊湊,有效果就行了,一直用的迷迷糊糊,這幾天工作比較閒,正好系統的總結梳理一下圖片壓縮方式: 圖片壓縮現在常見的有三種方式: 1、等比壓縮,等

Android呼叫巨集的方式

在工程專案中,為什麼vendor目錄下的platformconfigs.xml檔案可以對部分手機的屬性特性進行設定? 答:在framework目錄下,android.provider.Settings檔案為這種方式提供了可能。通過官方文件對其描述:“The Settings

AndroidIPC的幾方式詳細分析與優缺點分析

Android程序間通訊(IPC:Inter-Process Communication)的幾種主要方式如下 1.使用Bundle   ----> 用於android四大元件間的程序間通訊 android的四大元件都可使用Bundle傳遞資料  所以如果要實現四大元

Android 使用OpenCV的方式(Android Studio)

其實最早接觸OpenCV是很久很久之前的事了,大概在2013年的5,6月份,當時還是個菜逼(雖然現在也是個菜逼),在那一段時間,學了一段時間的android(並不算學,一個月都不到),之後再也沒接觸android,而是一直在接觸java web。那次接觸Open

Android進度條】方式實現自定義圓形進度條ProgressBar

總結了3種方法: 1.多張圖片切換 2.自定義顏色 3.旋轉自定義圖片 其它: Android自定義控制元件NumberCircleProgressBar(圓形進度條)的實現:點選開啟連結 橫線帶數字進度條:點選開啟連結

Android錄製音訊的方式

對於錄製音訊,Android系統就都自帶了一個小小的應用,但是使用起來可能不是特別的靈活,所以有提供了另外的倆種。 下邊來介紹下這三種錄製的方式; 1、通過Intent呼叫系統的錄音器功能,然後在錄製完成儲存以後在onActivityResult中返回錄製的音訊的uri,然

Android 音訊錄製 的方式

對於錄製音訊,Android系統就都自帶了一個小小的應用,可是使用起來可能不是特別的靈活。所以有提供了另外的倆種。下邊來介紹下這三種錄製的方式;1、通過Intent呼叫系統的錄音器功能,然後在錄製完畢儲存以後在onActivityResult中返回錄製的音訊的uri,然後通過

android事件處理的方式

android當中處理事件的三種方法:(這裡是按鈕的點選觸發的事件) 第一種方法: 建一個內部類    private  class ClickHandler implementsOnClickListener{                @Override     

maven打包方式

方法一:cmd 控制檯打包(比較不推薦)首先安裝maven外掛百度下載一個,配置環境變數什麼的~在cmd控制檯能mvn version能有資料出現。打包只需要到專案的根目錄下~在cmd敲入mvn package.不推薦原因:報錯後的報錯資訊比較不明確。方法二:eclipse或

方式實現自定義圓形頁面載入效果的進度條,包含一個好看的Android UI

效果圖如下:下載地址 樣式一、通過動畫實現定義res/drawable/loading.xml如下: <?xml version="1.0" encoding="UTF-8"?> ​ <animation-list android:oneshot=

Android解析json資料的方式

Json簡介 JAVAScript Object Notation是一種輕量級的資料交換格式 具有良好的可讀和便於快速編寫的特性。 業內主流技術為其提供了完整的解決方案(有點類似於正則表示式 ,獲得了

Android 方式實現自定義圓形頁面載入效果的進度條

一、通過動畫實現定義res/anim/loading.xml如下:<?xml version="1.0" encoding="UTF-8"?>  <animation-list android:oneshot="false"xmlns:android="ht

Android獲得Message物件方式的去唄

獲得Message物件的三種方式 1.new Message() 2.Message.obtain() 3.handler.obtainMessage() 1.new Message(); 這個方法沒什麼好多說的就是new一個物件 2.Message

方式求: 輸入一個整數,輸出該數二進制表示1的個數。其中負數用補碼表示

情況 temp 進制數 二進制表示 pac print 類型 solution 方式 package com.example; public class Solution { /* * 轉化成2進制數計算 */ public int NumberOf1(int n) {

Android之——ContentResolver查詢的方式

popu com 耗時 封裝 target 最適 token res 數據庫 轉載請註明出處:http://blog.csdn.net/l1028386804/article/details/47785491 今天做到一個小項目。查詢手機中短信的信息,當然得去系