android多渠道打包 脚本,Android 多渠道打包

现在Android多渠道打包普遍使用的是gradle设置productFlavor方式,通过gradle aR,可以执行一个命令,打出多个包,但是这种方式每次都要走一遍打包流程,而目前很多包仅仅是渠道号不一致,并不需要重新在走一遍编译,打包流程。

看了美团的解决方案,他们利用了签名的漏洞,在META-INF目录内添加空文件,可以不用重新签名应用,本文介绍了一种用户执行过gradle aR命令,自动运行渠道包生成脚本,打多个渠道包的方式。想要入门gradle脚本,请查看邓凡平大神的博客文章:http://blog.csdn.net/innost/article/details/48228651 。

以下是打包脚本:

apply plugin: 'com.android.application'

def versionNameString="1.0"

def versionCodeInt=1

def appName="打包测试"   //你的应用的名称

def  releaseApk='app/build/outputs/apk/app-release.apk'

def packageChannel(String versionName,String appName,String releaseApk){

try {

def stdout = new ByteArrayOutputStream()

exec {

//执行Python脚本

commandLine 'python',rootProject.getRootDir().getAbsolutePath()+"/app/mulit_channel.py",versionName,appName,releaseApk

standardOutput = stdout

}

return stdout.toString().trim()

}

catch (ignored) {

return "UnKnown";

}

}

android {

compileSdkVersion 22

buildToolsVersion "22.0.1"

defaultConfig {

applicationId "com.ndktest"

minSdkVersion 14

targetSdkVersion 22

versionCode versionCodeInt

versionName versionNameString

}

signingConfigs {

debug {

// No debug config

}

release {

storeFile file("../keystore/netstars.keystore")

storePassword "123456"

keyAlias "netstars.keystore"

keyPassword "123456"

}

}

buildTypes {

release {

buildConfigField "boolean", "LOG_DEBUG", "false"

minifyEnabled true

zipAlignEnabled true

// 移除无用的resource文件

shrinkResources true

signingConfig signingConfigs.release

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

debug {

minifyEnabled false

debuggable true

}

}

sourceSets {

main {

jniLibs.srcDirs = ['libs']

}

}

project.afterEvaluate {

//在Release执行以后

tasks.getByName("assembleRelease"){

it.doLast{

def rApk=new File(releaseApk);

if(rApk.exists()){

packageChannel(versionNameString,appName,rApk.absolutePath)

}

}

}

}

}

dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])

compile 'com.android.support:appcompat-v7:22.2.0'

}

Python脚本:

#!/usr/bin/python

# coding=utf-8

import zipfile

import shutil

import os

import datetime

import sys

# 空文件 便于写入此空文件到apk包中作为channel文件

src_empty_file = 'empty.txt'

# 创建一个空文件(不存在则创建)

f = open(src_empty_file, 'w')

f.close()

# 获取渠道列表

channel_file = 'channel.txt'

f = open(channel_file)

lines = f.readlines()

f.close()

src_apk=sys.argv[3]

# file name (with extension)

src_apk_file_name = os.path.basename(src_apk)

print(src_apk_file_name)

# 分割文件名与后缀

temp_list = os.path.splitext(src_apk_file_name)

# name without extension

src_apk_name = temp_list[0]

# 后缀名,包含.   例如: ".apk "

src_apk_extension = temp_list[1]

# 创建生成目录,与文件名相关

output_dir = '../output' + '/'

# 目录不存在则创建

if not os.path.exists(output_dir):

os.mkdir(output_dir)

# 遍历渠道号并创建对应渠道号的apk文件

for line in lines:

# 获取当前渠道号,因为从渠道文件中获得带有\n,所有strip一下

target_channel = line.strip()

#获取日期

now = datetime.datetime.now()

nowTime=now.strftime('%Y-%m-%d')

# 拼接对应渠道号的apk

length=len(sys.argv)

if length>1 :

target_apk = output_dir +sys.argv[2]+"v"+sys.argv[1]+"_"+nowTime+ "_" + target_channel + src_apk_extension

else:

target_apk = output_dir +src_apk_name + "_" + target_channel + src_apk_extension

# 拷贝建立新apk

shutil.copy(src_apk,  target_apk)

# zip获取新建立的apk文件

zipped = zipfile.ZipFile(target_apk, 'a', zipfile.ZIP_DEFLATED)

# 初始化渠道信息

empty_channel_file = "META-INF/channel_{channel}".format(channel = target_channel)

# 写入渠道信息

zipped.write(src_empty_file, empty_channel_file)

# 关闭zip流

zipped.close()

1.获取到渠道号:

import android.content.Context;

import android.content.SharedPreferences;

import android.content.SharedPreferences.Editor;

import android.content.pm.ApplicationInfo;

import android.content.pm.PackageManager.NameNotFoundException;

import android.preference.PreferenceManager;

import android.text.TextUtils;

import java.io.IOException;

import java.util.Enumeration;

import java.util.zip.ZipEntry;

import java.util.zip.ZipFile;

/***

*https://github.com/GavinCT/AndroidMultiChannelBuildTool

***/

public class ChannelUtil {

private static final String CHANNEL_KEY = "channel";

private static final String CHANNEL_VERSION_KEY = "channel_version";

private static String mChannel;

/**

* 返回市场。  如果获取失败返回""

* @param context

* @return

*/

public static String getChannel(Context context){

return getChannel(context, "");

}

/**

* 返回市场。  如果获取失败返回defaultChannel

* @param context

* @param defaultChannel

* @return

*/

public static String getChannel(Context context, String defaultChannel) {

//内存中获取

if(!TextUtils.isEmpty(mChannel)){

return mChannel;

}

//sp中获取

mChannel = getChannelBySharedPreferences(context);

if(!TextUtils.isEmpty(mChannel)){

return mChannel;

}

//从apk中获取

mChannel = getChannelFromApk(context, CHANNEL_KEY);

if(!TextUtils.isEmpty(mChannel)){

//保存sp中备用

saveChannelBySharedPreferences(context, mChannel);

return mChannel;

}

//全部获取失败

return defaultChannel;

}

/**

* 从apk中获取版本信息

* @param context

* @param channelKey

* @return

*/

private static String getChannelFromApk(Context context, String channelKey) {

//从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[] split = ret.split("_");

String channel = "";

if (split != null && split.length >= 2) {

channel = ret.substring(split[0].length() + 1);

}

return channel;

}

/**

* 本地保存channel & 对应版本号

* @param context

* @param channel

*/

private static void saveChannelBySharedPreferences(Context context, String channel){

SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);

Editor editor = sp.edit();

editor.putString(CHANNEL_KEY, channel);

editor.putInt(CHANNEL_VERSION_KEY, getVersionCode(context));

editor.commit();

}

/**

* 从sp中获取channel

* @param context

* @return 为空表示获取异常、sp中的值已经失效、sp中没有此值

*/

private static String getChannelBySharedPreferences(Context context){

SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);

int currentVersionCode = getVersionCode(context);

if(currentVersionCode == -1){

//获取错误

return "";

}

int versionCodeSaved = sp.getInt(CHANNEL_VERSION_KEY, -1);

if(versionCodeSaved == -1){

//本地没有存储的channel对应的版本号

//第一次使用  或者 原先存储版本号异常

return "";

}

if(currentVersionCode != versionCodeSaved){

return "";

}

return sp.getString(CHANNEL_KEY, "");

}

/**

* 从包信息中获取版本号

* @param context

* @return

*/

private static int getVersionCode(Context context){

try{

return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;

}catch(NameNotFoundException e) {

e.printStackTrace();

}

return -1;

}

}

友盟SDK中提供了通过代码设置渠道号的功能,结合上述打包脚本和获取脚本信息代码,相信多渠道打包问题基本可以得到解决了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值