Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android產品研發(五)--)多渠道打包

android產品研發(五)--)多渠道打包

編輯:關於Android編程

國內的Android開發者還是很苦逼的,由於眾所周知的原因,google play無法再國內打開,所以android系的應用市場,群雄爭霸,而後果就是國內存在著有眾多的應用市場,產品在不同的渠道可能有這不同的統計需求,為此android開發人員需要為每個應用市場發布一個安裝包,這裡就涉及到了android的多渠道打包。

本文主要講解的就是幾種主流的多渠道打包方式,以及其優劣勢。

通過配置gradle腳本實現多渠道打包

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

(一)在androidmanifest.xml中定義mate-data標簽

    
        

              

        
   

這裡需要注意的是:上面的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]  
        }  
    }  

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

manifestPlaceholders = [UMENG_CHANNEL_VALUE: "channel_name"]  

實現多渠道打包更換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...')

(三)打包一個正常的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;
    }
    /**
     * 從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;
    }
}

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

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

問題:
項目中由於使用了友盟統計,以前是在meta-data中保存渠道信息,現在更改了方式之後需要手動執行渠道號的設置代碼:

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

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

總結

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

本文以同步至github中:https://github.com/yipianfengye/androidProject,歡迎star和follow

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved