Android QA专用,Python实现不一样的多渠道打包工具
2017-01-17 10:50
846 查看
相对于美团打包方案,我为什么要写这个工具?
除了Gradle的多渠道打包,目前最流行的应该是美团(美团Android自动化之旅—生成渠道包、美团Android自动化之旅—适配渠道包、Walle)使用
Python直接添加渠道文件的打包方式了,速度真是杠杠的!但是,这里有一个问题:需要一个已签名无渠道号的
APK,那么问题来了,这个
APK哪里来的?懂行的朋友该说了,
Gradle随便打个
Release包不完事了嘛。是的,我也是这么想的。但是领导说
QA打包的时候需要改一部分配置文件代码(例如APP版本、后台环境、数据库版本bulabulabula),这样会带来潜在的问题。能不能直接通过命令行的形式打包,即
QA不需要改动任何代码,只需要设置不同的参数就好了。小脑瓜一转,有了。
Gradle可以。吭哧吭哧半天,做好了。然后
QA说:敲命令行多麻烦,还可能出错。能不能直接写个界面,我点点按钮就可以了。So,就有了这个
Python写的工具。我们暂时叫它
QA打包工具。
写在前面
为什么要有这个QA打包工具,这个工具存在的唯一目的就是根据
QA的选择,执行命令行打包,实际还是
Gradle打包。如果和您预期不同或者您已经一眼看穿这小把戏。左拐出门,走好了您馁。如果您对
Gradle配置参数打包或者对
Python如何实现感兴趣,请继续~
效果图
First blood 多渠道配置
第一滴血总让人满怀期待,完后只剩空虚寂寞冷。千篇一律的东西。这里以百度和豌豆荚为例,直接贴代码。android { productFlavors { wandoujia {} baidu {} productFlavors.all { flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] } } // 重命名生成的apk文件 applicationVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk') && variant.productFlavors.size() > 0) { File outputDirectory = new File(outputFile.parent); def fileName = "(" + variant.productFlavors[0].name + ")Test_${defaultConfig.versionName}_${releaseTime()}.apk" output.outputFile = new File(outputDirectory, fileName) } } } } def releaseTime() { return new Date().format("yyyyMMddHHmmss", TimeZone.getTimeZone("UTC")) }
Gradle设置参数后的打包命令
打所有渠道包gradlew assembleRelease -PSERVER_TYPE=1 -PIS_DEBUG=false -PMINIFYENABLED=false
打指定渠道包(以百度为例)
gradlew assembleBaiduRelease -PSERVER_TYPE=1 -PIS_DEBUG=false -PMINIFYENABLED=false
gradlew assebleXXXX是默认的打包方式,
-P后面是我们自己配置的各种参数。
Gradle参数配置
首先想一个问题,Gradle命令中的参数怎么就莫名其妙变成了我们程序可访问的参数。一个是
Gradle一个是
Android,真正实现了两不沾。破解也很简单,用文件连接。
Gradle在
Build时,会在
app/build/generated/source/buildconfig/渠道/release/包名/下生成
BuildConfig.java文件,就是这个文件实现了两者的通信。
Gradle设置及获取参数(划重点)
代码是最好的语言表述者。android { defaultConfig { applicationId "com.yikousamo.test" versionCode 1 versionName "1.0.0" minSdkVersion 14 targetSdkVersion 21 // 服务器类型 buildConfigField 'int', 'SERVER_TYPE', '1' // 是否开启调试,默认开启 buildConfigField 'boolean', 'IS_DEBUG', 'true' // 是否开启混淆,默认不混淆 buildConfigField 'boolean', 'MINIFYENABLED', 'false' } buildTypes { if (project.hasProperty('SERVER_TYPE') && project.hasProperty('IS_DEBUG') && project.hasProperty('MINIFYENABLED')){ release { buildConfigField 'int', 'SERVER_TYPE', SERVER_TYPE buildConfigField 'boolean', 'IS_DEBUG', IS_DEBUG buildConfigField 'boolean', 'MINIFYENABLED', MINIFYENABLED minifyEnabled Boolean.parseBoolean(MINIFYENABLED) zipAlignEnabled false shrinkResources false signingConfig signingConfigs.gliv proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } debug { minifyEnabled false zipAlignEnabled false shrinkResources false // 包名增加后缀,不同apk可以在相同设备上安装 applicationIdSuffix ".debug" signingConfig signingConfigs.gliv proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
这里配置了三个可以参数,分别是
SERVER_TYPE,
IS_DEBUG,
MINIFYENABLED,
defaultConfig里是对应的默认值。可以依据自己实际需求增加更多的参数。道理一样,不再赘述。
判断有没有指定参数的方法为
project.hasProperty('XXX'),下面就是设置到对应的参数中。这里有个小技巧,
debug设置
applicationIdSuffix相当于改包名,这样在同一部手机上可以同时存在
debug和
release包。具体可以看下我另一篇文章Android package属性、package name和Application ID三者的联系及区别。
设置完参数之后,在
Build时,在
BuildConfig中会生成对应的代码。
package com.yikousamo.test; public final class BuildConfig { public static final boolean DEBUG = false; public static final String APPLICATION_ID = "com.yikousamo.test"; public static final String BUILD_TYPE = "release"; public static final String FLAVOR = "baidu"; public static final int VERSION_CODE = 1; public static final String VERSION_NAME = "1.0.0"; // Fields from build type: release public static final int DB_VERSION = 1; public static final boolean IS_DEBUG = true; public static final boolean MINIFYENABLED = false; public static final int SERVER_TYPE = 1; // Fields from default config. }
注意下这里的包名是
APP的包名。这也就意味着我们可以在代码中引用这个类。例如,在
BaseApplication中设置引用
public static boolean isDebug = BuildConfig.IS_DEBUG。其余依据业务需要,同理。到这里就已经完全完成了多渠道打包各种参数的配置。接下来是
Python实现命令行打包。
Python打包工具实现思路
前文说过,Python(3.5.0版本)在这里唯一的作用是用界面替代
QA输入命令行。
Python执行命令行的方式有三种:
os.system("cmd")
subprocess.Popen
commands.getstatusoutput
作为
Python新手,三种方式的优劣我就不妄加评价了。凑合着用,反正在我眼里都是垃圾。这里采用第二种产生子进程的方式执行
cmd命令。界面实现采用的
tKinter。代码很简单,也没有多少行。直接放大了。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # 一口仨馍 import subprocess from tkinter import * from tkinter import messagebox from tkinter.ttk import Combobox from tkinter.filedialog import askdirectory import os root = Tk() root.title("Android渠道包") root.geometry('500x340') # 是x 不是* rootPath = StringVar() # 项目根目录 frm = Frame(root) #第一个字母直接大写,省去upperCase channels = ['Baidu', 'Wandoujia'] UN_SPECIAL_CHANNEL = '所有渠道包' def get_frame(): frame = Frame(frm, pady=3) frame.pack() return frame def get_entry(frm): entry = Entry(frm, width=12) entry.pack() return entry def get_combobox(frm, value): combobox = Combobox(frm, width=9) combobox["state"] = "readonly" # 只读 combobox['values'] = value # 设置下拉列表的值 combobox.current(0) # 设置下拉列表默认显示的值,0为 numberChosen['values'] 的下标值 combobox.pack() return combobox def get_label(frm, text): Label(frm, text=text, font=(17), width=14,anchor ='w', justify='left').pack(side=LEFT) def select_path(): path_ = askdirectory() rootPath.set(path_) # 选择根目录 frm_choose_root_dir = get_frame() rootPathEntry = Entry(frm_choose_root_dir, textvariable=rootPath, width=18) rootPathEntry.pack(side=LEFT) Button(frm_choose_root_dir, text="项目根目录", width=12, command=select_path).pack() frm_choose_root_dir.pack(side=TOP) # ServerType frm_server_type = get_frame() get_label(frm_server_type, 'ServerType:') ServerTypeCombox = get_combobox(frm_server_type, (0, 1, 2, 3)) # VersionCode frm_version_code = get_frame() get_label(frm_version_code, 'VersionCode:') VersionCodeEntry = get_entry(frm_version_code) # VersionName frm_version_name = get_frame() get_label(frm_version_name, 'VersionName:') VersionNameEntry = get_entry(frm_version_name) # IsDebug frm_is_debug = get_frame() get_label(frm_is_debug, 'IsDebug:') IsDebugComboBox = get_combobox(frm_is_debug, (True, False)) # DbVersion frm_db_version = get_frame() get_label(frm_db_version, 'DbVersion:') DbVersionEntry = get_entry(frm_db_version) # 混淆 frm_minifyenabled = get_frame() get_label(frm_minifyenabled, '混淆:') minifyenabledComboBox = get_combobox(frm_minifyenabled, (True, False)) # 指定渠道 frm_special_release = get_frame() get_label(frm_special_release, '渠道:') channels.insert(0, UN_SPECIAL_CHANNEL) SpecifyReleaseComboBox = get_combobox(frm_special_release, tuple(channels)) def click_confirm(): if(rootPathEntry.get().strip() == "" or VersionCodeEntry.get().strip() == "" or VersionNameEntry.get().strip() == "" or DbVersionEntry.get().strip() == ""): messagebox.askokcancel('提示', '干哈~不填完咋么打包~') return do_gradle() def do_gradle(): # 切换到项目根目录 os.chdir(rootPathEntry.get()) # 获取当前工作目录 print(os.getcwd()) if SpecifyReleaseComboBox.get() == UN_SPECIAL_CHANNEL: do_all_release() else: do_specify_release(SpecifyReleaseComboBox.get()) # 打指定渠道包 def do_specify_release(channel): cmd = 'gradlew assemble'+channel+'Release' \ ' -PSERVER_TYPE=' + ServerTypeCombox.get() + \ ' -PVERSION_CODE=' + VersionCodeEntry.get() + \ ' -PVERSION_NAME=' + VersionNameEntry.get() + \ ' -PDB_VERSION=' + DbVersionEntry.get() + \ ' -PIS_DEBUG=' + IsDebugComboBox.get().lower() + \ ' -PMINIFYENABLED=' + minifyenabledComboBox.get().lower() subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) # 打所有的渠道包 def do_all_release(): cmd = 'gradlew assembleRelease' \ ' -PSERVER_TYPE=' + ServerTypeCombox.get() + \ ' -PVERSION_CODE=' + VersionCodeEntry.get() + \ ' -PVERSION_NAME=' + VersionNameEntry.get() + \ ' -PDB_VERSION=' + DbVersionEntry.get() + \ ' -PIS_DEBUG=' + IsDebugComboBox.get().lower() + \ ' -PMINIFYENABLED=' + minifyenabledComboBox.get().lower() subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) Button(root, text='确定', width=12, command=lambda: click_confirm()).pack(side=BOTTOM) frm.pack() root.mainloop()
多渠道验证
假设现在已经打了一个豌豆荚的包。那么怎么验证是否真的改变了Anroidmanifest.xml中
UMENG_CHANNEL对应的值呢?也许你需要
ApkTool。
ApkTool可以反编译得到程序的源代码、图片、XML配置、语言资源等文件。这里我们只关心
Anroidmanifest.xml。
反编译步骤:
下载ApkTool
将需要反编译的APK文件放到该目录下,打开命令行界面 ,定位到apktool文件夹
输入命令:java -jar apktool_2.2.1.jar decode test.apk
之后发现在文件夹下多了个test文件夹,查看
Anroidmanifest.xml即可
相关文章推荐
- Android QA专用,Python实现不一样的多渠道打包工具
- android批量打包工具-python实现
- Android 使用Python实现多渠道打包实践
- Python实现Android Apk 加固及多渠道打包
- Android 使用Python实现多渠道打包
- 美团多渠道打包工具walle及结合python实现界面化快速打包
- 自制的android多渠道应用打包工具--RyApkTool(1)
- Android多渠道打包工具
- Android分渠道打包(Python 3.4 实现)
- 自制的android多渠道应用打包工具--RyApkTool(2)
- Android多渠道打包工具Gradle插件
- Android多渠道打包工具
- Android apk多渠道自动打包 - 不提供工具,只提供源码
- Android Stuido 实现多渠道打包
- Android打包之Ant多渠道打包的实现
- gradle打包android (实现外部导入签名文件、多渠道打包、导入ant脚本)
- android 多渠道打包---使用python 3.3.2
- Python实现的基于ADB的Android远程工具
- ant工具-多渠道自动打包android项目
- android多渠道打包工具