2016年4月6日
总体思路是把渠道信息放到apk里面的META-INF
文件夹下,然后用app进行读取
1.python代码
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import zipfile
import shutil
__author__ = "liucl"
class Builder:
def __init__(self, src_path='./build/outputs/apk', src_txt='.', tar_path='./build/outputs/apk'):
"""
src_path apk索引位置
src_txt 渠道信息位置
tar_path 目标apk位置
"""
self.src_path = src_path
self.file_path = src_txt
self.tar_path = tar_path
def compile_apk(self):
curr_path = os.getcwd()
os.chdir('..')
print('编译中...')
os.system('gradlew -q assembleRelease')
print('编译完成,打包中')
os.chdir(curr_path)
self.build()
def build(self):
# 打开apk
if os.path.exists(self.src_path):
apks_info = os.listdir(self.src_path)
for apk in apks_info:
if apk.find('release.apk') != -1:
self.open_apk(os.path.join(self.src_path, apk))
return
self.compile_apk()
else:
self.compile_apk()
def read_file(self):
channel_file = None
try:
channel_file = open(os.path.join(self.file_path, 'channel.txt'), 'r')
return channel_file.readlines()
except:
print('打开文件出错')
finally:
if channel_file:
channel_file.close()
def filter_file(self, file_name=[]):
if len(file_name) == 0:
raise Exception('请输入渠道信息')
return [channel.strip() for channel in file_name if
not channel.startswith('//') and channel.strip() != '' and channel.strip() != '\n']
def open_apk(self, apk_path):
if zipfile.is_zipfile(apk_path):
for apk_channel in self.filter_file(self.read_file()):
shutil.copyfile(apk_path, 'test.apk')
try:
zipped = zipfile.ZipFile('test.apk', 'a', zipfile.ZIP_DEFLATED)
empty_channel_file = "META-INF/ZNKE_PACKAGE"
zipped.writestr(empty_channel_file, apk_channel)
except:
os.remove('test.apk')
finally:
zipped.close()
apkname = apk_channel + '.apk'
shutil.move('test.apk', os.path.join(self.tar_path, apkname))
print('已完成%s' % apkname)
else:
raise Exception('请将apk拷到正确目录')
if __name__ == '__main__':
Builder().build()
2.获取渠道信息的java代码
/**
* 获取META-INFO下面的渠道信息
* @param context
* @param channelKey 渠道文件名 ‘ZNKE_PACKAGE’
* @return
*/
public static String getChannelFromApk(Context context, String channelKey) {
ApplicationInfo info = context.getApplicationInfo();
String sourceDir = info.sourceDir;
//注意这里:默认放在meta-inf/里, 所以需要再拼接一下
String key = "META-INF/" + channelKey;
ZipFile zipfile = null;
String channel = "";
BufferedReader bufferedReader = null;
try {
zipfile = new ZipFile(sourceDir);
Enumeration> entries = zipfile.entries();
InputStream is = null;
//获取文件流
while (entries.hasMoreElements()) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
String entryName = entry.getName();
if (entryName.contains(key)) {
is = zipfile.getInputStream(entry);
break;
}
}
//读取渠道信息
if (is != null) {
InputStreamReader reader = new InputStreamReader(is);
bufferedReader = new BufferedReader(reader);
channel = bufferedReader.readLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (zipfile != null) {
try {
zipfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader!= null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return channel;
}
3.关联UMENG的java代码
AnalyticsConfig.setChannel(channel);
实现原理:
按照以往的打包方式,首先你得有一个存储channel的文件,它里面存储所有渠道信息。你使用gradle把所有信息读出来,然后填到AndroidMainfests中供友盟使用。这样一来,每个渠道包都要重新打一次包,费时费力。其实美团有一篇文章说到,Android的apk下面有一个META-INF/
下面可以存储文件,然后在程序里面读取到,有了这种思路,我们可以事先把每个包的渠道信息存储到里面,然后在程序里面把他读取出来。这样你只需要打一个包就OK了。其实写到META-INF/的过程不一定要用Python写,用java也可以完成。有兴趣的人可以尝试下。
使用方式:
- 首先你的有python3.x的环境。
-
然后也要有channel信息的文件。上面使用的是channel.txt。把channel放到项目根目录。一行一个渠道比如下面:
- 把build.py放到主模块目录。注意不是根目录
- 这段java代码意思是把渠道信息从
META-INF/
读取出来。在友盟渠道包初始化的时候调用者java代码获取渠道信息,之后调用友盟的AnalyticsConfig.setChannel(channel);
完成渠道的获取。