Android批量打包-如何一秒內打完幾百個apk渠道包

在國內Android經常使用渠道可能多達幾十個,如: 
谷歌市場、騰訊應用寶、百度手機助手、91手機商城、360應用平臺、豌豆莢、安卓市場、小米、魅族商店、oppo手機、聯想樂商、中興匯天地、華爲、安智、應用匯、木螞蟻、3G安卓市場(久邦開發者發佈系統) 
uc應用商店、蘇寧應用、淘寶手機助手、蘑菇市場、搜狗市場、搜狗助手、機鋒、易用匯(金立手機)、中國聯通沃商、中國移動MM、中國電信天翼、億優市場、歷趣世界、冒泡堂、網訊安卓開發者平臺、桌樂、網易、泡椒網、十字貓、酷傳、安粉、安卓園、安卓之家 
因此在工做中,當項目開發、測試完畢後就須要針對不一樣的渠道打出對應的apk安裝包。爲了統計每一個渠道效果,咱們可使用Umeng sdk或者百度的sdk。這些sdk的使用我就再也不這裏贅述了,請看相應的開發文檔便可。本文以友盟統計爲例。python

批量打包方式一:Gradle方式

我相信如今應該不少開發環境都是AndroidStudio了,對Gradle相對仍是熟悉的。若是您使用的是Eclipse也沒有關係,用AndroidStudio導入Eclipse工程,或者把gradle配置放在Eclipse工程下(由於AndroidStudio和Eclipse的工程目錄有些差異,把對應的目錄配置對便可) 
首先咱們使用AndroidStudio新建一個工程,名叫AndroidBatchApk,工程結構以下: 
打開AndroidManifest.xml文件 添加友盟的渠道配置以下:android

這裏寫圖片描述

在MainActivity 顯示渠道名代碼:git

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String channel = ManifestUtil.getMetaDataFromAppication(this, "UMENG_CHANNEL");
        //String channel = ManifestUtil.getUmengChannel(this);
        ((TextView) findViewById(R.id.tv_channel)).setText(channel);
    }
}
  •  
<meta-data
            android:name="UMENG_APPKEY"
            android:value="Your UMENG_APPKEY" />
    <meta-data
            android:name="UMENG_CHANNEL"
            android:value="${UMENG_CHANNEL_VALUE}" /> //${UMENG_CHANNEL_VALUE}是個佔位符
  •  
打開app目錄下的build.gradle文件,修改爲以下形式:
apply plugin: 'com.android.application'
    android {
        compileSdkVersion 22
        buildToolsVersion "22.0.1"
        packagingOptions {
            exclude 'META-INF/NOTICE.txt'
            exclude 'META-INF/LICENSE.txt'
        }
        //簽名
        signingConfigs {
            release {
                //storeFile file("../yourapp.keystore")
                storeFile file("keystore_apk.jks")
                storePassword "123456"
                keyAlias "apk"
                keyPassword "123456"
            }
        }
        buildTypes {
            release {
                // 不顯示Log
                //buildConfigField "boolean", "LOG_DEBUG", "false"

                //minifyEnabled true //混淆
                zipAlignEnabled true //內存對齊
                shrinkResources true //移除無用的resource文件
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.release

                android.applicationVariants.all { variant ->
                    def stringsFile = new File(variant.outputs[0].processResources.assetsDir, "abc.txt")
                    stringsFile.mkdir()
                }

                applicationVariants.all { variant ->
                    variant.outputs.each { output ->
                        def outputFile = output.outputFile
                        if (outputFile != null && outputFile.name.endsWith('.apk')) {
                            def fileName = "APK_${releaseTime()}_${variant.productFlavors[0].name}.apk"
                            output.outputFile = new File(outputFile.parent, fileName)
                        }
                    }
                }
            }
        }

        lintOptions {
            checkReleaseBuilds false
            abortOnError false
            ignoreWarnings true
        }

        // 渠道列表
        productFlavors {
            _360 {}
            _91 {}
            QQ {}
            appChina {}
            baidu {}
            google {}
            //.....
        }

        productFlavors.all { flavor ->
            flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
        }
    }

    def releaseTime() {
        return new Date().format("yyyy-MM-dd HH-mm-ss", TimeZone.getTimeZone("GMT+8"))
    }

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:22.2.1'
    }
  •  

上面的配置,咱們測試打6個包,分別是google 、_360、 _9一、 appChina、 QQ、 baidugithub

打開cmd命令行 進入工程所在的目錄,輸入命令:gradle build 不出意外將看到以下成功界面: 
這裏寫圖片描述app

而且在output目錄下生成了咱們要的apk包(AndroidBatchApk\app\build\outputs\apk) 
這裏寫圖片描述ide

如今用的安裝咱們的生成apk文件,安裝google渠道的apk。 
這裏寫圖片描述工具

到這裏咱們就經過gradle方式成功的批量打包了,測試

時間咱們只花費了25秒,可是這是最簡單的工程,若是是實際的開發中,咱們的項目會很大,打包的時間也會花費很長時間,我如今公司的項目,經過這種方式打包,須要30、40分鐘左右,這也是挺長的。時間上並不佔優點。可是比咱們用工具一個個的打apk強太多了。下面爲你們界面一種更高效的打包方式。gradle

批量打包方式二:Python批量打包

首先配置好Python,我用的是Python2.7版本。使用該方式,不把渠道名稱放在AndroidManifest.xml 裏,而是新建一個空文件,文件名就是渠道名稱。該文件放在apk目錄的META-INF裏。META-INF目錄下默認文件列表以下: 
這裏寫圖片描述ui

如今咱們要解決兩個問題:

若是在META-INF目錄下新建文件?

咱們解決第一個問題。首先咱們經過AndroidStudio或者Eclipse打一個正式環境的apk安裝包,不須要有渠道。 
而後按照渠道列表 複製出各個渠道的,而後往apk文件裏寫入文件爲渠道名的空文件。咱們使用Python代碼來實現該功能,代碼以下:

import sys,os,shutil,zipfile,time
apkVersion="1.0"
srcFileName="source.apk"
destDir=os.path.abspath('.')
file=open("channel.txt")

def writeChannelToApk(filename,channel):
        z=zipfile.ZipFile(filename,'a',zipfile.ZIP_DEFLATED)
        empty_channel_file="META-INF/channel_{channe}".format(channe=channel)
        target_file="channel.apk"
        z.write(target_file,empty_channel_file)
        z.close()
        print "writeChannelToApkchannel"+channel+","+filename+"\n"

def cpFile(srcPath,fileName):
    destPath = destDir + os.path.sep + fileName
    if os.path.exists(srcPath) and not os.path.exists(destPath):
        shutil.copy(srcPath,destPath)



if not os.path.exists(srcFileName):
    print "sourcefile"+srcFileName+"notexists"
    sys.exit(1)


start = time.clock()

for line in file:
    channel=line.strip('\n').strip()
    targetFileName="apk_"+channel+"-"+apkVersion+".apk"
    print "copyfile:"+targetFileName
    cpFile(srcFileName,targetFileName)
    writeChannelToApk(targetFileName,channel)
end = time.clock()

print("The function run time is : %.03f seconds" %(end-start))
  •  

上面是我編寫的Python代碼,根據代碼咱們須要三個文件,一個咱們打出的apk文件(source.apk 固然名字能夠改)、一個空apk文件(channel.apk)和渠道列表文件(channel.txt) 目錄以下: 
這裏寫圖片描述

渠道文件內容以下: 
360 
appChina 
wandoujia 
91 
baidu 
QQ 
3G 
eoe 
anzhi 
163 
hiapk 
jifeng 
xiaomi 
meizu 
oppo 
lenovo

在命令行輸入:python batch_apk.py 回車

這裏寫圖片描述

瞬間完成: 
這裏寫圖片描述

解壓文件oppo渠道的apk,看看是否是META-INF下是否是有渠道文件: 
這裏寫圖片描述

若是讀取META-INF下的渠道文件?

public class ManifestUtil {

    public static String channel;

    public static String getUmengChannel(Context context) {
        //return getMetaDataFromAppication(context, "UMENG_CHANNEL");
        return getChannel(context);
    }

    /**
     * 獲取META-INFO下面的渠道
     * @param context
     * @return
     */
    public static String getChannel(Context context) {
        if (!TextUtils.isEmpty(channel)) {
            return channel;
        }
        ApplicationInfo appinfo = context.getApplicationInfo();
        String sourceDir = appinfo.sourceDir;
        ZipFile zipfile = null;
        final String start_flag = "META-INF/channel_";
        try {
            zipfile = new ZipFile(sourceDir);
            Enumeration<?> entries = zipfile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = ((ZipEntry) entries.nextElement());
                String entryName = entry.getName();
                if (entryName.contains(start_flag)) {
                    channel = entryName.replaceAll(start_flag, "");
                    return channel;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (zipfile != null) {
                try {
                    zipfile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return "";

    }
}
  •  

咱們安裝oppo渠道的apk看看可否讀取到渠道名: 
這裏寫圖片描述

經過上面的對比,使用Pyhon往META-INF寫入渠道的方式,比gradle方式極大的提升了效率。

資源下載

腳本地址Github

相關文章
相關標籤/搜索