使用apktool对andriod进行批量打包

背景:

在开发完APP后,需要对该产品投放在各个渠道进行推广,然而对数据的追踪至关重要,所以需要在安装包内批量打包渠道号,来达到数据收集的目的。

约定:

备注:在所有的参数中,不能使用中文

在输出的目录中,有三个子目录:

1、decode:反编译后的目录

2、re_package:打入渠道后回编的目录

3、sign_package:回编后的渠道包签名后的目录。

 

使用方式:

python main.py

–channels <渠道列表,以英文逗号,分隔,如baidu,web>

–package <安装包地址, 如: /user/local/1.apk>

–apk_tool <apktool工具地址, 如: /user/local/apktool_2.3.0.jar>

–output_dir <解压后地址, 如: /user/local/output>

–jarsigner <签名工具的目录, 如: /user/local/jarsigner.exe>

–key_store_file <key_store_file地址, 如: /user/local/gamecenter.jks

–key_store_pass<key_store_pass密码, 如: 123456

–key_pass<key_pass密码, 如: 123456

–key_alias<key_alias密码, 如: gamecenter

–tsacert<是否启用时间戳签发机构,默认不启用0, 0:不启用,1:启用(需要翻墙)>

–andriod_name<AndroidManifest.xml的android:name,默认为CHANNEL>

如果觉得配置太繁琐,可以自行修改main.py 内的默认值

eg:

python main.py –channels baidu,common –package  /user/local/1.apk –apk_tool /user/local/apktool_2.3.0.jar –output_dir /user/local/apktool_2.3.0.jar –jarsigner /user/local/jarsigner.exe –key_store_file /user/local/gamecenter.jks –key_store_pass 123456 –key_pass 123456 –key_alias gamecenter

 

版本:

python

python = 2.7.11

apk_tool

apk_tool = 2.3.0

https://ibotpeaches.github.io/Apktool/

java

java = 1.8.0_111

jarsigner

jarsigner path <java setupapth>/bin/jarsigner.exe

 

main.py文件内容:

# /usr/bin/python
# -*- coding:utf-8 -*-

# 在所有的配置参数中,不能使用中文(包括配置路径)

import os
import time
import argparse

curr_dir = os.path.join(os.getcwd(), time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time())))

jar_signer_file_def = "D:\\jdk\\setuppath\\bin\\jarsigner.exe"
key_store_file_def = "E:\\you\\path\\gamecenter.jks"
key_store_pass_def = "xxx"
key_pass_def = "xxx"
key_alias_def = "xxx"
apk_tool_def = 'E:\\you\\path\\apktool_2.3.0.jar'


def build():
    parser = argparse.ArgumentParser()
    parser.add_argument('--channels', default='')
    parser.add_argument('--package', default='')
    parser.add_argument('--apk_tool', default=apk_tool_def)
    parser.add_argument('--output_dir', default=curr_dir)
    parser.add_argument('--jarsigner', default=jar_signer_file_def)
    parser.add_argument('--key_store_file', default=key_store_file_def)
    parser.add_argument('--key_store_pass', default=key_store_pass_def)
    parser.add_argument('--key_pass', default=key_pass_def)
    parser.add_argument('--key_alias', default=key_alias_def)
    parser.add_argument('--tsacert', default='0')
    parser.add_argument('--andriod_name', default=andriod_name_def)

    args = parser.parse_args()
    channels = args.channels.strip()
    apk_file = args.package.strip()
    apk_tool = args.apk_tool.strip()
    output_dir = args.output_dir.strip()
    jar_signer_file = args.jarsigner.strip()
    key_store_file = args.key_store_file.strip()
    key_store_pass = args.key_store_pass.strip()
    key_pass = args.key_pass.strip()
    key_alias = args.key_alias.strip()
    decode_dir = os.path.join(output_dir, 'decode')
    re_build_dir = os.path.join(output_dir, 're_package')
    signed_apk_dir = os.path.join(output_dir, 'sign_package')
    tsacert = args.tsacert.strip()
    andriod_name = args.andriod_name.strip()

    if output_dir == '':
        raise Exception('output_dir is empty,please use --output_dir %s' % curr_dir)

    if os.path.exists(output_dir):
        raise Exception('output_dir is exists, please choose another.')

    if channels == '':
        raise Exception('channels is empty,please use --channels 1,2,3')

    if apk_file == '':
        raise Exception('package path is empty,please use --package E:\\app-debug.apk')

    if apk_tool == '':
        raise Exception('apk_tool path is empty,please use --apk_tool %s' % apk_tool_def)

    if jar_signer_file == '':
        raise Exception('jarsigner is empty,please use --jarsigner %s' % jar_signer_file_def)

    if key_store_file == '':
        raise Exception('key_store_file is empty,please use --key_store_file %s' % key_store_file_def)

    if key_store_pass == '':
        raise Exception('key_store_pass is empty,please use --key_store_pass %s' % key_store_pass_def)

    if key_pass == '':
        raise Exception('key_pass is empty,please use --key_pass %s' % key_pass_def)

    if key_alias == '':
        raise Exception('key_alias is empty,please use --key_alias %s' % key_alias_def)

    if andriod_name == '':
        raise Exception('andriod_name is empty,please use --andriod_name %s' % andriod_name_def)

    try:
        # 创建输出目录
        os.mkdir(output_dir)
        # 创建最后签名的目录
        os.mkdir(signed_apk_dir)

        array_channels = channels.split(",")

        if len(array_channels) <= 0:
            raise Exception('channels format is not valid,please use --channels 1,2,3')

        print 'begin decode apk,please waiting...'
        # 解压压缩包
        os.system("java -jar %s d %s -o %s -f" % (apk_tool, apk_file, decode_dir))

        # 安装包文件名
        package_name = os.path.splitext(os.path.basename(apk_file))[0]

        print 'completed decode apk.'

        for channel in array_channels:

            channel = channel.strip()

            channel_info = '<meta-data android:name="%s" android:value="%s"/>\r\n' % (andriod_name, channel)

            print 'channel: %s is running to replace AndroidManifest.xml, please waiting...' % channel

            manifest_file = os.path.join(decode_dir, 'AndroidManifest.xml')

            temp_manifest = []
            exists = False
            with open(manifest_file, 'r') as handle:
                for line in handle.readlines():
                    if line.find(andriod_name) > 0:
                        exists = True
                        temp_manifest.append(channel_info)
                    else:
                        if not exists and line.find('</application>') >= 0:
                            temp_manifest.append(channel_info)

                        temp_manifest.append(line)

            with open(manifest_file, 'w') as handle:
                handle.write("".join(temp_manifest))

            print 'channel: %s replaced AndroidManifest.xml completed' % channel

            print 'channel: %s is building...' % channel
            # 重新打包
            re_apk_file = os.path.join(re_build_dir, '%s_%s.apk' % (package_name, channel))
            os.system("java -jar %s b %s -o %s" % (apk_tool, decode_dir, re_apk_file))
            print 'channel: %s build completed' % channel

            print 'channel: %s is signing...' % channel
            # 签名
            signed_apk_file = os.path.join(signed_apk_dir, '%s_%s.apk' % (package_name, channel))

            # 时间戳签发机构,由于国内,翻墙经常会断,看情况而定,不会影响使用
            tsacert = ('' if tsacert == '0' else '-tsa https://timestamp.geotrust.com/tsa')

            os.system("%s -keystore %s -storepass %s -keypass %s -sigfile CERT -digestalg SHA1 -sigalg MD5withRSA -signedjar %s %s %s %s" % (jar_signer_file, key_store_file, key_store_pass, key_pass, signed_apk_file, re_apk_file, key_alias, tsacert))
            print 'channel: %s sign completed' % channel

        print 'all completed'

    except Exception as e:
        print e


if __name__ == '__main__':
    build()

 

github地址:https://github.com/qqingdou/andriod_batch_packing

Leave a Comment