gitee地址:[https://gitee.com/chenpanpan95/python_tools.git]
优化了一下网上现有的v1打包分包的脚本
v1打包分包的思路是:将代渠道号命名的文件,用zip打入apk签名目录下,安卓代码里筛选标记后读到渠道信息
但是目前各大应用商店强推targetSdkVersion 30,只能用v2签名的apk,那么v1分包的脚本思路就不能用了,因为v2签名目录不允许被更改
新思路写入渠道号的方式很多,比如腾讯分包是在代码注释区插入渠道信息等
我是通过反编译apk,然后再包内容路径assets/data下,添加一个channel.json文件,然后安卓代码里获取channel.json里的渠道号(反编译是多的步骤,其实可以直接用python代码 zipped.write()直接插入到目录assets/data下,但是秉着学习的态度,多此一举的加入了反编译的步骤,以方便后续进一步拓展功能)
脚本没有写自动签名的功能,分包后需要再签名,这个自动签名懒得写了,毕竟apk加固工具里面都有批量签名的功能
以下是脚本运行需要的文件
1.apktool.jar用于反编译再打包,我放在tools里了(文件网上下一个就行,命名需要为apktool.jar,放到tools里,也可以改脚本代码实现个性化)
2.需要写入渠道号的apk,脚本可以检索多个apk,分别打包配置的所有渠道号
3 channel.txt 用于配置渠道号,每个渠道号需换行填写
4.python脚本源码,我用的python3.8,运行如有问题,请安装3.8或以上的版本(我用vscode看日志调试)
运行脚本后
多出几个文件夹
apktool_前缀的是对应apk反编译后的目录
new_apk 是写入渠道后的apk集所在
贴一个获取渠道的代码
public static String getJsonDataFromAsset(Context mContext, String fileName) {
try {
InputStream is = mContext.getAssets().open(fileName);
int length = is.available();
byte[] buffer = new byte[length];
is.read(buffer);
String result = new String(buffer, "utf8");
return result;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
private TaskActionInfoBean getChannelID(int tid) {
try {
String js = getJsonDataFromAsset(context, "json/channel.json");
//用gson解析一下js就行了
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
以下是python源码
#!/usr/bin/python
# coding=utf-8
import subprocess
import os
import json
import shutil
#获取当前文件(.py)所在的文件夹路径
current_path = os.path.dirname(os.path.abspath(__file__))
print("current_path:"+current_path)
#获取当前文件(.py)目录中所有的apk源包
src_apks = []
for file in os.listdir(current_path):
if os.path.isfile(current_path+"/"+file):
extension = os.path.splitext(file)[1][1:]
if extension in 'apk':
src_apks.append(current_path+"/"+file)
f = open(current_path+'/channel.txt')
lines = f.readlines()
f.close()
# 创建一个空文件(不存在则创建)
channel_file = current_path+'/channel.json'
#批量反编译写入渠道打包
def w_channel_apk(output_dir):
output_apk = current_path+'/new_apk/'
for src_apk in src_apks:
# file name (with extension)
src_apk_file_name = os.path.basename(src_apk)
print("src_apk_file_name:"+src_apk_file_name)
temp_list = os.path.splitext(src_apk_file_name)
# 分割文件名与后缀
src_apk_name = temp_list[0]
output_dir = current_path+'/apktool_' + src_apk_name + '/'
print("output_dir:"+output_dir)
if os.path.exists(current_path+'/apktool_' + src_apk_name):
shutil.rmtree(current_path+'/apktool_' + src_apk_name)
tool_path = current_path+'/tools/'
apk_file = current_path+'/'+src_apk_file_name
command = f"java -jar {tool_path}apktool.jar d -f {apk_file} -o {output_dir}"
result = subprocess.run(command, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print("Error decompiling APK:")
print(result.stderr)
else:
print("APK 反编译 成功!")
#读取渠道列表
for line in lines:
target_channel = line.strip()
print("target_channel:---"+target_channel)
jsondata = {"channel": target_channel}
json_data = json.dumps(jsondata)
f = open(channel_file, 'w')
f.write(json_data)
f.close()
print("json文件写入成功!---"+channel_file)
shutil.copy(channel_file, output_dir+'/assets/data/channel.json')
print("复制json到指定目录!---"+output_dir+'/assets/data/channel.json')
if not os.path.exists(output_apk):
os.mkdir(output_apk)
new_apk_path = output_apk+'new_'+target_channel+'_'+src_apk_name+'.apk'
print("new_apk_path:--"+new_apk_path)
if os.path.exists(new_apk_path):
os.remove(new_apk_path)
command2 = f"java -jar {tool_path}apktool.jar b {output_dir} -o {new_apk_path}"
result1 = subprocess.run(command2, shell=True, capture_output=True, text=True)
if result1.returncode != 0:
print("Error decompiling APK:")
print(result.stderr)
else:
print("APK 重新打包 成功!")
#批量反编译打包
def decompile_apk(apk_file, output_dir):
output_apk = current_path+'/new_apk/'
for src_apk in src_apks:
# file name (with extension)
src_apk_file_name = os.path.basename(src_apk)
print("src_apk_file_name:"+src_apk_file_name)
temp_list = os.path.splitext(src_apk_file_name)
# 分割文件名与后缀
src_apk_name = temp_list[0]
output_dir = current_path+'/apktool_' + src_apk_name + '/'
if os.path.exists(output_dir):
os.remove(output_dir)
tool_path = current_path+'/tools/'
command = f"java -jar {tool_path}apktool.jar d -f {apk_file} -o {output_dir}"
result = subprocess.run(command, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print("Error decompiling APK:")
print(result.stderr)
else:
print("APK 反编译 成功!")
if not os.path.exists(output_apk):
os.mkdir(output_apk)
new_apk_path = output_apk+'new_'+src_apk_name+'.apk'
print("new_apk_path:--"+new_apk_path)
# if os.path.exists(new_apk_path):
# os.remove(new_apk_path)
command2 = f"java -jar {tool_path}apktool.jar b {output_dir} -o {new_apk_path}"
result1 = subprocess.run(command2, shell=True, capture_output=True, text=True)
if result1.returncode != 0:
print("Error decompiling APK:")
print(result.stderr)
else:
print("APK 重新打包 成功!")
# decompile_apk(current_path+"/app.apk", current_path+"/apktool_")
w_channel_apk(current_path+"/apktool_")
再贴一个v1写入渠道的源码(学习时网上找的)
#!/usr/bin/python
# coding=utf-8
import zipfile
import shutil
import os
# 空文件 便于写入此空文件到apk包中作为channel文件
src_empty_file = '/Users/Administrator/Desktop/PythonTool/czt.txt'
# 创建一个空文件(不存在则创建)
f = open(src_empty_file, 'w')
f.close()
# 获取当前目录中所有的apk源包
src_apks = []
# python3 : os.listdir()即可,这里使用兼容Python2的os.listdir('.')
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)
# 获取渠道列表
channel_file = '/Users/Administrator/Desktop/PythonTool/channel.txt'
f = open(channel_file)
lines = f.readlines()
f.close()
for src_apk in src_apks:
# file name (with extension)
src_apk_file_name = os.path.basename(src_apk)
# 分割文件名与后缀
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_' + src_apk_name + '/'
# 目录不存在则创建
if not os.path.exists(output_dir):
os.mkdir(output_dir)
# 遍历渠道号并创建对应渠道号的apk文件
for line in lines:
# 获取当前渠道号,因为从渠道文件中获得带有\n,所有strip一下
target_channel = line.strip()
# 拼接对应渠道号的apk
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/cychannel_{channel}".format(channel = target_channel)
# 写入渠道信息
zipped.write(src_empty_file, empty_channel_file)
# 关闭zip流
zipped.close()