apk批量多渠道打包,支持v1v2,python自动化脚本

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()


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值