vasDollyPath: “…/VasDolly.jar”
]
// 360加固配置
packers = [account : properties[‘ACCOUNT360’], //账号
password : properties[‘PASSWORD360’], //密码
zipPath : “
p
r
o
j
e
c
t
.
r
o
o
t
D
i
r
/
j
i
a
g
u
/
360
j
i
a
g
u
.
z
i
p
"
,
/
/
加固压缩包路径
u
n
z
i
p
P
a
t
h
:
"
{project.rootDir}/jiagu/360jiagu.zip", //加固压缩包路径 unzipPath : "
project.rootDir/jiagu/360jiagu.zip",//加固压缩包路径unzipPath:"{project.rootDir}/jiagu/360jiagubao/”, //加固解压路径
jarPath : “
p
r
o
j
e
c
t
.
r
o
o
t
D
i
r
/
j
i
a
g
u
/
360
j
i
a
g
u
b
a
o
/
j
i
a
g
u
/
j
i
a
g
u
.
j
a
r
"
,
/
/
执行命令的
j
a
r
包路径
c
h
a
n
n
e
l
C
o
n
f
i
g
P
a
t
h
:
"
{project.rootDir}/jiagu/360jiagubao/jiagu/jiagu.jar", //执行命令的jar包路径 channelConfigPath: "
project.rootDir/jiagu/360jiagubao/jiagu/jiagu.jar",//执行命令的jar包路径channelConfigPath:"{project.rootDir}/jiagu/Channel.txt”, //加固多渠道
jiagubao_mac : “https://down.360safe.com/360Jiagu/360jiagubao_mac.zip”, //加固mac下载地址
jiagubao_windows : “https://down.360safe.com/360Jiagu/360jiagubao_windows_64.zip” //加固widnows下载地址
]
gradle相关基础
- gradle脚本插件的引用
apply from: “${project.rootDir}/packers.gradle”
- 局部变量
def dest = “A”
- 扩展属性
使用ext扩展块,一次扩展多个属性
ext {
account = “XXXX”
password = “XXXXX”
}
- 字符串相关
单引号不支持插值
def name = ‘张三’
双引号支持插值
def name = “我是${‘张三’}”
三个单引号支持换行
def name = “”"
张三
李四
“”"
- 可有可无的圆括号
// 这两种写法等价
println(‘A’)
println ‘A’
- 闭包作为方法的最后一个参数
repositories {
println “A”
}
repositories() { println “A” }
repositories({println “A” })
- task依赖
task B {
// TaskB依赖TaskA,故会先执行TaskA
dependsOn A
//其次执行packersRelease
doLast {
println “B”
}
}
- task排序
//taskB必须总是在 taskA 之后运行, 无论 taskA 和 taskB 是否将要运行
taskB.mustRunAfter(taskA)
//没有msut那么严格
taskB.shouldRunAfter (taskA)
- 文件定位
// 使用一个相对路径
File configFile = file(‘src/config.xml’)
// 使用一个绝对路径
configFile = file(configFile.absolutePath)
// 使用一个项目路径的文件对象
configFile = file(new File(‘src/config.xml’))`
- 文件遍历
// 对文件集合进行迭代
collection.each {File file ->
println file.name
}
- 文件复制重命名
copy {
from 源文件地址
into 目标目录地址
rename(“原文件名”, “新文件名字”)
}
自动上传到服务器
这个功能准备在下篇文章更新,我们可以通过curl命令上传到自己的服务器,如果你在测试阶段可以上传到蒲公英或者fir.im托管平台,目前他们都提供了相关的操作方式,这样基本上整个自动化的目的就完成了,当然你也可以选择Jenknis自动化构建、打包及上传。
- 发布应用到fir.im托管平台 入口
方式一:fir-CLI 命令行工具上传
$ fir p path/to/application -T YOUR_FIR_TOKEN
方式二:API 上传
通过curl命令调用相关的api
1.获取凭证
curl -X “POST” “http://api.bq04.com/apps”
-H “Content-Type: application/json”
-d “{“type”:“android”, “bundle_id”:“xx.x”, “api_token”:“aa”}”
2.上传apk
curl -F “key=xxxxxx”
-F “token=xxxxx”
-F “file=@aa.apk”
-F “x:name=aaaa”
-F “x:version=a.b.c”
-F “x:build=1”
-F “x:release_type=Adhoc” \ #type=ios 使用
-F “x:changelog=first”
https://up.qbox.me
- 发布应用到蒲公英 入口
curl -F “file=@/tmp/example.ipa” -F “uKey=” -F “_api_key=” https://upload.pgyer.com/apiv1/app/upload
整体效果
我们的需求是需要打两批包,用于老后台与新后台,老后台的包必须加上app-前缀,所以有三个任务packersNewRelease
执行正常的加固打包用于新后台,packersOldRelease
用于打包加前缀app-名称用于老后台,packersRelease
这个任务用于一键同时打包成老后台与新后台。
同时可以在gradle控制台查看打包任务的输出日志,如下:
gradle自动化源码
为了能够让大家尝试自动化gradle脚本带来的便利之处,下面我贡献上自己的整个gradle源码,需要的可以拿走去研究,如存在问题也希望多多交流。
/**
- @author hule
- @date 2020/04/15 13:42
- description:360自动加固+Vaslloy多渠道打包
*/
// 把敏感信息存放到自定义的properties文件中
def propertiesFile = rootProject.file(“release.properties”)
def properties = new Properties()
properties.load(new FileInputStream(propertiesFile))
ext {
// 签名配置
signing = [keyAlias : properties[‘RELEASE_KEY_ALIAS’],
keyPassword : properties[‘RELEASE_KEY_PASSWORD’],
storeFile : properties[‘RELEASE_KEYSTORE_PATH’],
storePassword: properties[‘RELEASE_STORE_PASSWORD’]
]
// app相关的配置
app = [
//默认release apk的文件路径,因为加固是基于release包的
releasePath : “
p
r
o
j
e
c
t
.
b
u
i
l
d
D
i
r
/
o
u
t
p
u
t
s
/
a
p
k
/
r
e
l
e
a
s
e
"
,
/
/
对
r
e
l
e
a
s
e
a
p
k
加固后产生的加固
a
p
k
地址
p
a
c
k
e
r
s
P
a
t
h
:
"
{project.buildDir}/outputs/apk/release", //对release apk 加固后产生的加固apk地址 packersPath : "
project.buildDir/outputs/apk/release",//对releaseapk加固后产生的加固apk地址packersPath:"{project.buildDir}/outputs/packers”,
//加固后进行腾讯多渠道打包的地址
channelPath : “${project.buildDir}/outputs/channels”,
//腾讯VasDolly多渠道打包jar包地址
vasDollyPath: “…/VasDolly.jar”
]
// 360加固配置
packers = [account : properties[‘ACCOUNT360’], //账号
password : properties[‘PASSWORD360’], //密码
zipPath : “
p
r
o
j
e
c
t
.
r
o
o
t
D
i
r
/
j
i
a
g
u
/
360
j
i
a
g
u
.
z
i
p
"
,
/
/
加固压缩包路径
u
n
z
i
p
P
a
t
h
:
"
{project.rootDir}/jiagu/360jiagu.zip", //加固压缩包路径 unzipPath : "
project.rootDir/jiagu/360jiagu.zip",//加固压缩包路径unzipPath:"{project.rootDir}/jiagu/360jiagubao/”, //加固解压路径
jarPath : “
p
r
o
j
e
c
t
.
r
o
o
t
D
i
r
/
j
i
a
g
u
/
360
j
i
a
g
u
b
a
o
/
j
i
a
g
u
/
j
i
a
g
u
.
j
a
r
"
,
/
/
执行命令的
j
a
r
包路径
c
h
a
n
n
e
l
C
o
n
f
i
g
P
a
t
h
:
"
{project.rootDir}/jiagu/360jiagubao/jiagu/jiagu.jar", //执行命令的jar包路径 channelConfigPath: "
project.rootDir/jiagu/360jiagubao/jiagu/jiagu.jar",//执行命令的jar包路径channelConfigPath:"{project.rootDir}/jiagu/Channel.txt”, //加固多渠道
jiagubao_mac : “https://down.360safe.com/360Jiagu/360jiagubao_mac.zip”, //加固mac下载地址
jiagubao_windows : “https://down.360safe.com/360Jiagu/360jiagubao_windows_64.zip” //加固widnows下载地址
]
}
/**
- 360加固,适用于新后台打包
*/
task packersNewRelease {
group ‘packers’
dependsOn ‘assembleRelease’
doLast {
//删除加固后的渠道包
deleteFile()
// 下载360加固文件
download360jiagu()
// 寻找打包文件release apk
def releaseFile = findReleaseApk()
if (releaseFile != null) {
//执行加固签名
packers360(releaseFile)
//对加固后的apk重新用腾讯channel构建渠道包
reBuildChannel()
} else {
println ‘packers===can’t find release apk and can’t excute 360 jiagu’
}
}
}
/**
- 适用于老后台,老后台需要在渠道apk的名称增加前缀 app-
*/
task packersOldRelease {
group ‘packers’
doLast {
File channelFile = file(“KaTeX parse error: Expected '}', got 'EOF' at end of input: …elFile = file("{app[“channelPath”]}/old”)
if (!oldChannelFile.exists()) {
oldChannelFile.mkdirs()
}
// 对文件集合进行迭代
channelFile.listFiles().each { File file ->
copy {
from file.absolutePath
into oldChannelFile.absolutePath
rename(file.name, “app-${file.name}”)
}
}
println ‘packers===packersOldRelease sucess’
}
}
}
/**
- 加固后,打新版本的渠道包时,同时生成老版本的渠道包
*/
task packersRelease {
group ‘packers’
dependsOn packersNewRelease
dependsOn packersOldRelease
packersOldRelease.mustRunAfter(packersNewRelease)
doLast {
println “packers===packersRelease finished”
}
}
/**
- 对于release apk 进行360加固
*/
def packers360(File releaseApk) {
println ‘packers=beginning 360 jiagu’
def packersFile = file(app[“packersPath”])
if (!packersFile.exists()) {
packersFile.mkdir()
}
exec {
// 登录360加固保
executable = ‘java’
args = [‘-jar’, packers[“jarPath”], ‘-login’, packers[“account”], packers[“password”]]
println 'packers=import 360 login’
}
exec {
// 导入签名信息
executable = ‘java’
args = [‘-jar’, packers[“jarPath”], ‘-importsign’, signing[“storeFile”],
signing[“storePassword”], signing[“keyAlias”], signing[“keyPassword”]]
println ‘packers=import 360 sign’
}
exec {
// 查看360加固签名信息
executable = ‘java’
args = [‘-jar’, packers[“jarPath”], ‘-showsign’]
println 'packers=show 360 sign’
}
exec {
// 初始化加固服务配置,后面可不带参数
executable = ‘java’
args = [‘-jar’, packers[“jarPath”], ‘-config’]
println ‘packers=init 360 services’
}
exec {
// 执行加固
executable = ‘java’
args = [‘-jar’, packers[“jarPath”], ‘-jiagu’, releaseApk.absolutePath, app[“packersPath”], ‘-autosign’]
println 'packers=excute 360 jiagu’
}
println 'packers=360 jiagu finished’
println "packers=360 jiagu path ${app[“packersPath”]}"
}
/**
- 自动下载360加固保,也可以自己下载然后放到根目录
*/
def download360jiagu() {
// 下载360压缩包
File zipFile = file(packers[“zipPath”])
if (!zipFile.exists()) {
if (!zipFile.parentFile.exists()) {
zipFile.parentFile.mkdirs()
println("packers=create parentFile jiagu ${zipFile.parentFile.absolutePath}")
}
// 加固保的下载地址
def downloadUrl = isWindows() ? packers[“jiagubao_windows”] : packers[“jiagubao_mac”]
// mac自带curl命令 windows需要下载curl安装
def cmd = “curl -o ${packers[“zipPath”]} ${downloadUrl}”
println cmd
cmd.execute().waitForProcessOutput(System.out, System.err)
}
File unzipFile = file(packers[“unzipPath”])
if (!unzipFile.exists()) {
//解压 Zip 文件
ant.unzip(src: packers[“zipPath”], dest: packers[“unzipPath”], encoding: “GBK”)
println 'packers=unzip 360jiagu’
//将解压后的文件开启读写权限,防止执行 Jar 文件没有权限执行,windows需要自己手动改
if (!isWindows()) {
def cmd = “chmod -R 777 ${packers[“unzipPath”]}”
println cmd
cmd.execute().waitForProcessOutput(System.out, System.err)
}
}
}
/**
- 腾讯channel重新构建渠道包
*/
def reBuildChannel() {
File channelFile = file(“${app[“channelPath”]}/new”)
if (!channelFile.exists()) {
channelFile.mkdirs()
}
def cmd = “java -jar ${app[“vasDollyPath”]} put -c ${”…/channel.txt"} ${outputpackersApk()} ${channelFile.absolutePath}"
println cmd
cmd.execute().waitForProcessOutput(System.out, System.err)
println ‘packers===excute VasDolly reBuildChannel’
}
/**
- 是否是windows系统
- @return
*/
static Boolean isWindows() {
return System.properties[‘os.name’].contains(‘Windows’)
}
/**
- 寻找本地的release apk
- @return true
*/
def deleteFile() {
delete app[“channelPath”]
delete app[“packersPath”]
println ‘packers===delete all file’
}
尾声
最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。
当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。
进阶学习视频
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。
当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。
进阶学习视频
[外链图片转存中…(img-IhWSkmfS-1720080311532)]
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-HfMM5gAp-1720080311533)]