walle多渠道打包可视化辅助工具---可选自动对齐、签名、校验、打渠道标志
!!!解放双手神器:使用Python写的一个美团多渠道打包可视化辅助工具!!!
使用前:
1、本可视化工具基于V2签名的美团多渠道walle(瓦力)打包,不同于传统gradle配置flavor渠道形式,walle打包的速度达到秒级,瞬间打几十上百个包不在话下,大大节省apk发布的时间有木有!!
https://segmentfault.com/a/1190000015554496
2、不过在进行360加固或其他加固的时候,还是有点小坑,需要重新对齐、签名、校验签名,才能继续打多渠道包。
3、GitHub上有哥们写了Python命令版的生成工具,但是单独使用某个环节功能的时候,就不太方便了。
https://github.com/Jay-Goo/ProtectedApkResignerForWalle/blob/master/ApkResigner.py
于是乎便有了本文的可视化工具:
(1)Build Tools Path:build-tools版本路径,主要是为了获取该目录下的zipalign和apksigner工具
(2)Before Apk Path:待处理的apk路径
(3)After Apk Dir:生成apk的文件夹路径
(4)Generate Apk Name:生成的Apk的名字
(5)Keystore Path:签名文件的路径
(6)Keystore Password:签名的密码
(7)Zipalign:apk对齐
(8)Sign:apk签名
(9)Check Sign:签名查询
(10)Multi Pack:多渠道打包
(11)Generate:生成
(12)Save Config:保存配置
具体代码实现如下:https://github.com/AssonChou/Walle-Auto-packing
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# coding: utf8
import os
from Tkinter import *
import subprocess
import json
import tkMessageBox
class GUI:
# 定义GUI界面
def __init__(self, root, param, function):
root.title("Walle AutoPacking")
Label(root, width=30, text="Build Tools Path", justify="left").grid(column=0, row=0, pady=10, sticky=E)
Entry(root, width=60, textvariable=param.build_tools_path, bd=1).grid(column=1, row=0, padx=10)
Label(root, width=30, text="Before Apk Path", justify="left").grid(column=0, row=1, pady=10, sticky=E)
Entry(root, width=60, textvariable=param.pre_apk_path, bd=1).grid(column=1, row=1, padx=10)
Label(root, width=30, text="After Apk Dir", justify="left").grid(column=0, row=2, pady=10, sticky=E)
Entry(root, width=60, textvariable=param.after_apk_path, bd=1).grid(column=1, row=2, padx=10)
Label(root, width=30, text="Generate Apk Name", justify="left").grid(column=0, row=3, pady=10, sticky=E)
Entry(root, width=60, textvariable=param.app_name, bd=1).grid(column=1, row=3, padx=10)
Label(root, width=30, text="Keystore Path", justify="left").grid(column=0, row=4, pady=10, sticky=E)
Entry(root, width=60, textvariable=param.keystore_path, bd=1).grid(column=1, row=4, padx=10)
Label(root, width=30, text="Keystore Password", justify="left").grid(column=0, row=5, pady=10, sticky=E)
Entry(root, width=60, textvariable=param.password, bd=1).grid(column=1, row=5, padx=10)
Checkbutton(root, text="(1) Zipalign ", variable=param.is_zipalign).grid(column=0, row=6, sticky=E)
Checkbutton(root, text="(2) Sign ", variable=param.is_sign).grid(column=1, row=6)
Checkbutton(root, text="(3) Check Sign", variable=param.is_check_sign).grid(column=0, row=7, sticky=E)
Checkbutton(root, text="(4) Multi Pack", variable=param.is_multi_pack).grid(column=1, row=7)
Button(root, height=1, width=28, text="Generate", command=lambda: function.select()).grid(column=1, row=8,
sticky=W, pady=10,
padx=10)
Button(root, height=1, width=28, text="Save Config", command=lambda: function.save()).grid(column=1, row=9,
sticky=W, pady=20,
padx=10)
class Params:
def __init__(self):
self.is_zipalign = BooleanVar()
self.is_sign = BooleanVar()
self.is_multi_pack = BooleanVar()
self.is_check_sign = BooleanVar()
self.build_tools_path = StringVar()
self.pre_apk_path = StringVar()
self.after_apk_path = StringVar()
self.app_name = StringVar()
self.walle_jar_path = StringVar()
self.channel_txt_path = StringVar()
self.check_jar_path = StringVar()
self.keystore_path = StringVar()
self.password = StringVar()
current_path = os.getcwd()
print "current_path:" + current_path
self.config_path = current_path + "\\config.json"
config_file = open(self.config_path, 'r')
try:
json_str = config_file.read()
if json_str.strip() == '':
return
self.channel_txt_path.set(current_path + "\\channel.txt")
self.check_jar_path.set(current_path + "\\CheckAndroidV2Signature.jar")
self.walle_jar_path.set(current_path + "\\walle-cli-all.jar")
self.config_json = json.loads(json_str)
self.is_zipalign.set(self.config_json["is_zipalign"])
self.is_sign.set(self.config_json["is_sign"])
self.is_check_sign.set(self.config_json["is_check_sign"])
self.is_multi_pack.set(self.config_json["is_multi_pack"])
self.build_tools_path.set(self.config_json["build_tools_path"])
self.pre_apk_path.set(self.config_json["pre_apk_path"])
self.after_apk_path.set(self.config_json["after_apk_path"])
self.app_name.set(self.config_json["app_name"])
self.keystore_path.set(self.config_json["keystore_path"])
self.password.set(self.config_json["password"])
finally:
if config_file:
config_file.close()
class Funtion:
def __init__(self, root):
self.parmas = Params()
self.gui = GUI(root, self.parmas, self)
def get_generate_apk(self):
generate_dir_path = self.parmas.after_apk_path.get() + "\\" + self.parmas.app_name.get()
if not os.path.exists(generate_dir_path):
os.mkdir(generate_dir_path)
return generate_dir_path + "\\" + self.parmas.app_name.get() + ".apk"
def zipalign(self):
print self.parmas.pre_apk_path.get()
if not os.path.exists(self.parmas.pre_apk_path.get()):
return 2
generate_apk_path = self.get_generate_apk()
order = self.parmas.build_tools_path.get() + "\zipalign -v 4 " + self.parmas.pre_apk_path.get() + " " + generate_apk_path
print "------------------zipaligning-----------------" + order
p = subprocess.Popen(order, shell=True, stdout=subprocess.PIPE)
out, err = p.communicate()
for line in out.splitlines():
# print line
if line.find("Verification succesful") >= 0:
return 1
return 2
def sign(self):
generate_apk_path = self.get_generate_apk()
if os.path.exists(generate_apk_path):
dest_apk_path = generate_apk_path
else:
dest_apk_path = self.parmas.pre_apk_path.get()
order = self.parmas.build_tools_path.get() + "\\apksigner sign --ks " + self.parmas.keystore_path.get() + " --ks-pass pass:" + self.parmas.password.get() + " --out " + dest_apk_path + " " + dest_apk_path
print "------------------signing-----------------" + order
os.system(order)
return 1
def check_sign(self):
generate_apk_path = self.get_generate_apk()
if not os.path.exists(generate_apk_path):
generate_apk_path = self.parmas.pre_apk_path.get()
order = "java -jar " + self.parmas.check_jar_path.get() + " " + generate_apk_path
print "------------------check_signing-----------------" + order
p = subprocess.Popen(order, shell=True, stdout=subprocess.PIPE)
out, err = p.communicate()
for line in out.splitlines():
print line
if line.find("{") >= 0:
try:
response = json.loads(line)
if (response["ret"] == 0) and response["isV2"] and response["isV2OK"]:
if __name__ == '__main__':
return 1
except Exception as e:
print e
return 2
def multi_pack(self):
generate_apk_path = self.get_generate_apk()
if os.path.exists(generate_apk_path):
dest_apk_path = generate_apk_path
else:
dest_apk_path = self.parmas.pre_apk_path.get()
order = "java -jar " + self.parmas.walle_jar_path.get() + " batch -f " + self.parmas.channel_txt_path.get() + " " + dest_apk_path
print "------------------multi_packing-----------------" + order
os.system(order)
return 1
def select(self):
result = 0
if self.parmas.is_zipalign.get():
result = self.zipalign()
if result != 2 and self.parmas.is_sign.get():
result = self.sign()
if result != 2 and self.parmas.is_check_sign.get():
result = self.check_sign()
if result != 2 and self.parmas.is_multi_pack.get():
result = self.multi_pack()
if result == 0:
tkMessageBox.showerror("Tips", "Please select the option!")
elif result == 1:
tkMessageBox.showinfo("Tips", "success")
elif result == 2:
tkMessageBox.showerror("Tips", "fail")
def save(self):
save_file = open(self.parmas.config_path, 'w')
try:
self.parmas.config_json["is_zipalign"] = self.parmas.is_zipalign.get()
self.parmas.config_json["is_sign"] = self.parmas.is_sign.get()
self.parmas.config_json["is_check_sign"] = self.parmas.is_check_sign.get()
self.parmas.config_json["is_multi_pack"] = self.parmas.is_multi_pack.get()
self.parmas.config_json["build_tools_path"] = self.parmas.build_tools_path.get()
self.parmas.config_json["pre_apk_path"] = self.parmas.pre_apk_path.get()
self.parmas.config_json["after_apk_path"] = self.parmas.after_apk_path.get()
self.parmas.config_json["app_name"] = self.parmas.app_name.get()
self.parmas.config_json["keystore_path"] = self.parmas.keystore_path.get()
self.parmas.config_json["password"] = self.parmas.password.get()
save_file.write(json.dumps(self.parmas.config_json))
save_file.close()
tkMessageBox.showinfo("Tip", "Save Success")
except Exception as e:
print e
save_file.close()
tkMessageBox.showerror("Error", "Save Fail!!!")
window = Tk()
Funtion(window)
window.mainloop()
如何使用?
1、全选表示一键进行对齐,签名,校验、生成多渠道包;
2、可多选或单选,可以单独进行单个操作,针对不同操作,目标apk的路径不同,如未生成过(Generate Apk Name).apk,则会针对Before Apk进行操作,如果已生成,则对(Generate Apk Name).apk进行操作。
3、点击save按钮保存配置