/** * Automatically download 360 reinforcement protection, or you can download it yourself and put it in the root directory */ def download360jiagu() { // Download 360 compressed package File zipFile = file(packers["zipPath"]) if (!zipFile.exists()) { if (!zipFile.parentFile.exists()) { zipFile.parentFile.mkdirs() println("packers===create parentFile jiagu ${zipFile.parentFile.absolutePath}") } // Download address of reinforcement protection def downloadUrl = isWindows() ? packers["jiagubao_windows"] : packers["jiagubao_mac"] // mac comes with curl command. windows needs to download curl installation def cmd = "curl -o ${packers["zipPath"]} ${downloadUrl}" println cmd cmd.execute().waitForProcessOutput(System.out, System.err) } File unzipFile = file(packers["unzipPath"]) if (!unzipFile.exists()) { //Unzip Zip the Zip file ant.unzip(src: packers["zipPath"], dest: packers["unzipPath"], encoding: "GBK") println 'packers===unzip 360jiagu' //Open the read-write permission of the extracted file to prevent the execution of Jar file without permission. If windows does not have permission, it needs to change it manually if (!isWindows()) { def cmd = "chmod -R 777 ${packers["unzipPath"]}" println cmd cmd.execute().waitForProcessOutput(System.out, System.err) } } }
Make a release package
gradle actually provides us with a series of related tasks, as shown in the figure below
We need to get a release package before strengthening, so we can use assemblyrelease to execute the Task of assemblyrelease before strengthening.
task packersNewRelease { group 'packers' //You can use the dependency of task to perform packaging first dependsOn 'assembleRelease' }
Automatic reinforcement
The so-called automatic reinforcement is nothing more than a few lines of commands. 360 reinforcement insurance provides a set of Command line reinforcement
In particular, there is a bug in the optional enhanced service configured by 360 here, which has been communicated with the official. They need to fix it in the next version. The current version 3.2 2.3 (March 16, 2020). Currently, the command line cannot only select piracy monitoring
/** * 360 reinforcement for release apk */ def packers360(File releaseApk) { println 'packers===beginning 360 jiagu' def packersFile = file(app["packersPath"]) if (!packersFile.exists()) { packersFile.mkdir() } exec { // Login 360 reinforcement insurance executable = 'java' args = ['-jar', packers["jarPath"], '-login', packers["account"], packers["password"]] println 'packers===import 360 login' } exec { // Import signature information executable = 'java' args = ['-jar', packers["jarPath"], '-importsign', signing["storeFile"], signing["storePassword"], signing["keyAlias"], signing["keyPassword"]] println 'packers===import 360 sign' } exec { // View 360 signature information executable = 'java' args = ['-jar', packers["jarPath"], '-showsign'] println 'packers===show 360 sign' } exec { // Initialize the reinforcement service configuration without parameters executable = 'java' args = ['-jar', packers["jarPath"], '-config'] println 'packers===init 360 services' } exec { // Execute reinforcement, and then automatically sign. If automatic signature is not adopted, you need to sign by yourself through build tools command 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"]}" }
Automatic signature
As for automatic signature, in fact, 360 provides the configuration option of automatic signature during reinforcement. If you don't want to upload the signature file to 360, you can choose to sign manually after reinforcement, because it involves security issues. I adopt 360 automatic signature in this version. If you want to sign manually, I'll give you a set of scheme below, We mainly use ziphalign and apksigner commands, which are located in the build tools directory in the SDK file. We need gradle to configure the path to execute automatic signature.
- Align unsigned apk s
zipalign -v -p 4 my-app-unsigned.apk my-app-unsigned-aligned.apk
- Sign apk with your private key
apksigner sign --ks my-release-key.jks --out my-app-release.apk my-app-unsigned-aligned.apk
- Verify that the apk has been signed
apksigner verify my-app-release.apk
Automatic implementation of multi-channel based on Apk
As for multi-channel packaging, we have always used Tencent's VasDolly in our previous projects, so we take the VasDolly command this time, but we need to download it first VasDolly.jar , there is no requirement on where to put it. Just configure the path in gradle. I put it directly in the project root directory. You can also use 360 multi-channel reinforcement. In fact, the whole set can use the commands provided by 360 reinforcement.
/** * Rebuild channel package for Tencent 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' }
Sensitive information access
We all know that signing requires signature files, passwords, aliases and other files, and 360 reinforcement needs to configure accounts and passwords. These are sensitive information. google officials do not recommend directly putting them in gradle. It is recorded in gradle in plain text, and it is recommended to store them in the properties file.
// Store the sensitive information in the custom properties file def propertiesFile = rootProject.file("release.properties") def properties = new Properties() properties.load(new FileInputStream(propertiesFile)) ext { // Signature configuration signing = [keyAlias : properties['RELEASE_KEY_ALIAS'], keyPassword : properties['RELEASE_KEY_PASSWORD'], storeFile : properties['RELEASE_KEYSTORE_PATH'], storePassword: properties['RELEASE_STORE_PASSWORD'] ] // app related configuration app = [ //The default is the file path of release apk, because reinforcement is based on the release package releasePath : "${project.buildDir}/outputs/apk/release", //Reinforcement APK address generated after reinforcement of release apk packersPath : "${project.buildDir}/outputs/packers", //Address for Tencent multi-channel packaging after reinforcement channelPath : "${project.buildDir}/outputs/channels", //Tencent VasDolly multi-channel packaging jar package address vasDollyPath: "../VasDolly.jar" ] // 360 reinforced configuration packers = [account : properties['ACCOUNT360'], //account number password : properties['PASSWORD360'], //password zipPath : "${project.rootDir}/jiagu/360jiagu.zip", //Reinforced package path unzipPath : "${project.rootDir}/jiagu/360jiagubao/", //Reinforcement decompression path jarPath : "${project.rootDir}/jiagu/360jiagubao/jiagu/jiagu.jar", //jar package path to execute the command channelConfigPath: "${project.rootDir}/jiagu/Channel.txt", //Reinforced multi-channel jiagubao_mac : "https://down.360safe.com/360Jiagu/360jiagubao_mac.zip ", / / Mac download address jiagubao_windows : "https://down.360safe.com/360Jiagu/360jiagubao_windows_64.zip "/ download address of widnows ]
gradle related Foundation
- Reference to the gradle script plug-in
apply from: "${project.rootDir}/packers.gradle"
- local variable
def dest = "A"
- Extended properties
use ext An extension block that extends multiple attributes at a time ext { account = "XXXX" password = "XXXXX" }
- String correlation
Single quotation marks do not support interpolation def name = 'Zhang San' Double quotes support interpolation def name = "I am ${'Zhang San'}" Three single quotes support line breaks def name = """ Zhang San Li Si """
- Optional parentheses
// The two expressions are equivalent println('A') println 'A'
- Closure as the last parameter of the method
repositories { println "A" } repositories() { println "A" } repositories({println "A" })
- task dependency
task B { // Task B depends on task a, so it will execute task a first dependsOn A //Secondly, execute packersRelease doLast { println "B" } }
- task sorting
//taskB must always run after taskA, regardless of whether taskA and taskB will run taskB.mustRunAfter(taskA) //Not as strict as msut taskB.shouldRunAfter (taskA)
- File location
// Use a relative path File configFile = file('src/config.xml') // Use an absolute path configFile = file(configFile.absolutePath) // A file object that uses a project path configFile = file(new File('src/config.xml'))`
- File traversal
// Iterate over a collection of files collection.each {File file -> println file.name }
- File copy rename
copy { from Source file address into Destination directory address rename(""Original file name", "New file name") }
Auto upload to server
This function will be updated in the next article. We can upload it to our server through curl command. If you are in the test phase, you can upload it to dandelion or fir Im hosting platform. At present, they all provide relevant operation methods, which basically completes the purpose of the whole automation. Of course, you can also choose Jenknis to automatically build, package and upload.
- Publish and apply to fir Im hosting platform entrance
Mode 1: fir-CLI Command line tool upload $ fir p path/to/application -T YOUR_FIR_TOKEN Mode 2: API upload adopt curl Command call related api 1.Get voucher curl -X "POST" "http://api.bq04.com/apps" \ -H "Content-Type: application/json" \ -d "{\"type\":\"android\", \"bundle_id\":\"xx.x\", \"api_token\":\"aa\"}" 2.upload 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 usage -F "x:changelog=first" \ https://up.qbox.me
- Publish apply to Dandelion entrance
curl -F "file=@/tmp/example.ipa" -F "uKey=" -F "_api_key=" https://upload.pgyer.com/apiv1/app/upload
Overall effect
Our requirement is to make two batches of packages for the old background and the new background. The packages of the old background must be prefixed with app. Therefore, there are three tasks: packersNewRelease performs normal reinforcement packaging for the new background, packersOldRelease for packaging prefixed app name for the old background, and packersRelease for packaging the old background and the new background at the same time.
At the same time, you can view the output log of the packaging task on the gradle console, as follows:
gradle automation source code
In order to let you try the convenience brought by automated gradle script, I'll contribute my whole gradle source code. You can take it away and study it if necessary. If there are problems, I hope to communicate more.
/** * @author hule * @date 2020/04/15 13:42 * description:360 Automatic reinforcement + Vaslloy multi-channel packaging */ // Store the sensitive information in the custom properties file def propertiesFile = rootProject.file("release.properties") def properties = new Properties() properties.load(new FileInputStream(propertiesFile)) ext { // Signature configuration signing = [keyAlias : properties['RELEASE_KEY_ALIAS'], keyPassword : properties['RELEASE_KEY_PASSWORD'], storeFile : properties['RELEASE_KEYSTORE_PATH'], storePassword: properties['RELEASE_STORE_PASSWORD'] ] // app related configuration app = [ //The default is the file path of release apk, because reinforcement is based on the release package releasePath : "${project.buildDir}/outputs/apk/release", //Reinforcement APK address generated after reinforcement of release apk packersPath : "${project.buildDir}/outputs/packers", //Address for Tencent multi-channel packaging after reinforcement channelPath : "${project.buildDir}/outputs/channels", //Tencent VasDolly multi-channel packaging jar package address vasDollyPath: "../VasDolly.jar" ] // 360 reinforced configuration packers = [account : properties['ACCOUNT360'], //account number password : properties['PASSWORD360'], //password zipPath : "${project.rootDir}/jiagu/360jiagu.zip", //Reinforced package path unzipPath : "${project.rootDir}/jiagu/360jiagubao/", //Reinforcement decompression path jarPath : "${project.rootDir}/jiagu/360jiagubao/jiagu/jiagu.jar", //jar package path to execute the command channelConfigPath: "${project.rootDir}/jiagu/Channel.txt", //Reinforced multi-channel jiagubao_mac : "https://down.360safe.com/360Jiagu/360jiagubao_mac.zip ", / / Mac download address jiagubao_windows : "https://down.360safe.com/360Jiagu/360jiagubao_windows_64.zip "/ download address of widnows ] } /** * 360 Reinforced, suitable for new background packaging */ task packersNewRelease { group 'packers' dependsOn 'assembleRelease' doLast { //Delete the reinforced channel package deleteFile() // Download 360 reinforcement files download360jiagu() // Find the package file release apk def releaseFile = findReleaseApk() if (releaseFile != null) { //Execute hardening signature packers360(releaseFile) //For the strengthened apk, build the channel package with Tencent channel again reBuildChannel() } else { println 'packers===can\'t find release apk and can\'t excute 360 jiagu' } } } /** * It is applicable to the old background. The old background needs to add the prefix app to the name of the channel apk- */ task packersOldRelease { group 'packers' doLast { File channelFile = file("${app["channelPath"]}/new") if (!channelFile.exists() || !channelFile.listFiles()) { println 'packers==== please excute pakcersNewRelease first!' } else { File oldChannelFile = file("${app["channelPath"]}/old") if (!oldChannelFile.exists()) { oldChannelFile.mkdirs() } // Iterate over a collection of files channelFile.listFiles().each { File file -> copy { from file.absolutePath into oldChannelFile.absolutePath rename(file.name, "app-${file.name}") } } println 'packers===packersOldRelease sucess' } } } /** * After reinforcement, when playing the new version of channel package, the old version of channel package is generated at the same time ### Full preparation for the interview Some basic knowledge and theories must be memorized and understood. Summarize them in your own language and memorize them. although Android It's not as hot as it was a few years ago. It's past the era when the four components can find high paying jobs. This only shows Android Positions below the intermediate level are saturated,**There is still a shortage of senior engineers**,I can obviously feel that there are many senior positions after the national day, so it is the most important to strive to become a senior engineer. Well, I hope it will help you. The next step is to sort out some Android Learning materials,**Interested friends can pay attention to my free collection method**. **①Android Notes on developing core knowledge points** **②Benchmarking "Ali" P7" 40W+Annual salary enterprise senior architect growth and learning Roadmap** ![](https://img-blog.csdnimg.cn/img_convert/2ad271593a0763a9a3744a3ea0b70e92.png) **③Interview highlights summary** ![](https://img-blog.csdnimg.cn/img_convert/f7c05c10c074b5894afcfeab36ce915d.png) **④Full set of systematic advanced architecture video** **Android After learning, it is even more powerful!**March BATJ Big factories, etc. (prepare for war)! Now it is said that the cold winter of the Internet is nothing more than that you get on the wrong car and wear less (skills). If you get on the right car, your own technical ability is strong enough and the cost of replacing the company is high, how can you be laid off? It is all to eliminate the end business Curd nothing more! Nowadays, the market is flooded with junior programmers. This tutorial is aimed at Android Development Engineer 1-6 The staff in is in a bottleneck period and wants to break through their salary increase and advance in the next year Android Middle and senior architects are like fish in water for you. Get it quickly! ![](https://img-blog.csdnimg.cn/img_convert/29643721be267e1382b28f61152f7233.png) **[CodeChina Open source projects:< Android Summary of study notes+Mobile architecture video+Real interview questions for large factories+Project practice source code](https://codechina.csdn.net/m0_60958482/android_p7)** G1wWMJ-1630557998519)] **③Interview highlights summary** [External chain picture transfer...(img-Nn3Lijds-1630557998520)] **④Full set of systematic advanced architecture video** **Android After learning, it is even more powerful!**March BATJ Big factories, etc. (prepare for war)! Now it is said that the cold winter of the Internet is nothing more than that you get on the wrong car and wear less (skills). If you get on the right car, your own technical ability is strong enough and the cost of replacing the company is high, how can you be laid off? It is all to eliminate the end business Curd nothing more! Nowadays, the market is flooded with junior programmers. This tutorial is aimed at Android Development Engineer 1-6 The staff in is in a bottleneck period and wants to break through their salary increase and advance in the next year Android Middle and senior architects are like fish in water for you. Get it quickly! [External chain picture transfer...(img-x8kcVyxG-1630557998521)] **[CodeChina Open source projects:< Android Summary of study notes+Mobile architecture video+Real interview questions for large factories+Project practice source code](https://codechina.csdn.net/m0_60958482/android_p7)**