之前發佈的文章:Gradle 自定義Plugin插件之上傳APK到蒲公英。讓我們知道了自定義插件的一些功能。
一般來說,在發佈的時候,我們的APK都需要加固的,畢竟安全點。這裏,我們就結合之前的插件,再定義一個加固APK的任務。然後,讓我們自動化的步伐可以更大一點。打包–→加固–→上傳蒲公英。
既然是,自動化的部署,我們肯定就不能用圖形化的加固操作工具了。
操作步驟:
- 一、下面加固的包
- 二、創建加固的Task任務
- 三、修改插件的Extension擴展
- 四,修改上傳Task的apk路徑
- 五、修改我們插件的依賴關係,讓其可以實現 打包–→加固–→上傳蒲公英
- 六、項目調用
一、下載360加固保
既然是加固,首先,我們需要到360官網註冊,並下載加固保:官方網站 (騰訊的加固好像不支持本地上傳APK)。
解壓目錄
這裏,我們要通過命令行執行jiagu.jar來完成我們的自動化apk加固。
help.txt裏面是一些加固的命令,有興趣的,可以都試試。
有了360加固保以後,就要開始編寫我們的加固Task任務了。
二、創建加固的Task任務
加固Task的創建跟上傳Task一樣,都是繼承DefaultTask,然後,在方法上通過註解@TaskAction 來讓我們的任務執行。
創建加固任務,我們主要做下面幾個事:
- 1,給我們加固Task設置一個組,這裏放到跟上傳Task一個組裏。
- 2,創建一個Model類,存放加固時需要的一些參數
比如, 加固jar包文件,賬號密碼;簽名信息;apk加固前,加固後路徑等。 - 3,執行加固時,獲取這個Model類的參數信息
- 4,執行360加固所需要的命令
比如,登陸,導入簽名,加固,設置自動簽名等等
public class ReinforceTask extends DefaultTask {
private BaseVariant mVariant;
private Project mTargetProject;
//加固相關的信息
public static class ReinforceModel {
//加固包的位置(jiagu.jar) 及登錄賬號密碼
public File reinforceJarFile;
public String reinforceUsername;
public String reinforcePassword;
//簽名文件 密碼等信息
public File signStoreFile;
public String signPassword;
public String signKeyAlias;
public String signKeyPassword;
//打包apk路徑及加固後apk的路徑
public File inputApkFile;
public File outputApkFile;
}
public void init(BaseVariant variant, Project project) {
this.mVariant = variant;
this.mTargetProject = project;
setDescription("reinforce for apk");
setGroup(TestJavaPlugin.PLUGIN_EXTENSION_NAME);
}
@TaskAction
public void reinforceApk() {
System.out.println("==============start JiaGu");
ReinforceModel request = initReinforceModel();
String loginCmdData = "java -jar %s -login %s %s";
String loginExec = String
.format(loginCmdData, request.reinforceJarFile, request.reinforceUsername,
request.reinforcePassword);
String loginMessage = exec(loginExec);
System.out.println("==========exe JiaGu Result: " + loginMessage);
String signCmdData = "java -jar %s -importsign %s %s %s %s";
String signExec = String
.format(signCmdData, request.reinforceJarFile, request.signStoreFile,
request.signPassword,
request.signKeyAlias, request.signKeyPassword);
//導入簽名
String signResult = exec(signExec);
System.out.println("===========exe JiaGu Sign Result: " + signResult);
//自動簽名
String suffixCmd = " -autosign ";
if (!request.outputApkFile.exists()) {
request.outputApkFile.mkdirs();
}
//jiagu.jar ,input apk file path, output apk dir
String reinforceCmdData = "java -jar %s -jiagu %s %s";
String reinforceExec = String
.format(reinforceCmdData, request.reinforceJarFile, request.inputApkFile,
request.outputApkFile);
//reinforce
String jiaguResult = exec(reinforceExec + suffixCmd);
System.out.println("=========exe APK JiaGu Result : " + jiaguResult);
}
@NotNull
private ReinforceModel initReinforceModel() {
Extension extension = Extension.getConfig(mTargetProject);
ReinforceModel request = new ReinforceModel();
request.outputApkFile = extension.outputFile;
request.reinforceJarFile = extension.reinforceJarFile;
request.reinforceUsername = extension.reinforceUsername;
request.reinforcePassword = extension.reinforcePassword;
NamedDomainObjectContainer<SigningConfig> signingConfigs = ((AppExtension) mTargetProject
.getExtensions().findByName(TestJavaPlugin.ANDROID_EXTENSION_NAME)).getSigningConfigs();
if (signingConfigs == null) {
throw new IllegalArgumentException("please config your sign info.");
}
for (SigningConfig config : signingConfigs) {
request.signStoreFile = config.getStoreFile();
request.signPassword = config.getStorePassword();
request.signKeyAlias = config.getKeyAlias();
request.signKeyPassword = config.getKeyPassword();
}
for (BaseVariantOutput output : mVariant.getOutputs()) {
request.inputApkFile = output.getOutputFile();
if (request.inputApkFile == null || !request.inputApkFile.exists()) {
throw new GradleException("apk file is not exist!");
}
}
if (request.signStoreFile == null || !request.signStoreFile.exists()) {
throw new IllegalArgumentException("please config your sign info.");
}
return request;
}
private String exec(String command) {
StringBuilder resultBuilder = new StringBuilder();
Process pro = null;
BufferedReader input = null;
Runtime runTime = Runtime.getRuntime();
if (runTime == null) {
throw new NullPointerException("reinforce task failed,Runtime is null");
}
try {
pro = runTime.exec(command);
input = new BufferedReader(new InputStreamReader(pro.getInputStream()));
String line;
while ((line = input.readLine()) != null) {
resultBuilder.append(line).append("\n");
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (pro != null) {
pro.destroy();
}
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return resultBuilder.toString();
}
}
這裏我們看到:
1,通過ReinforceModel來保存加固需要的參數信息。
2,在初始化init()方法裏設置了分組
3,在initReinforceModel()方法裏面,獲取我們加固需要的賬號,簽名等等信息。
4,通過reinforceApk()方法,就會分別執行,登陸,導入簽名,加固,自動簽名命令。
到這裏,我們自定義的加固Task就完成了。
下面,我們就結合之前的自動上傳到蒲公英Task任務,來實現 打包—加固–上傳 ,這個流程。
三、修改插件的Extension擴展
要完成打包–加固–上傳的流程,我們需要修改上篇文章中的Extension類,把360加固需要的參數添加上,來確保我們可以完成加固任務。
public class Extension {
public String uKey;
public String apiKey;
public String appName;
//==========加固相關的信息
//指向加固的jar包
public File reinforceJarFile;
//登陸用戶名
public String reinforceUsername;
//登陸密碼
public String reinforcePassword;
//輸出apk的目錄
public File outputFile;
public Extension() {
}
public Extension(String uKey, String apiKey, String appName, File reinforceJarFile,
String reinforceUsername, String reinforcePassword, File outputFile) {
this.uKey = uKey;
this.apiKey = apiKey;
this.appName = appName;
this.reinforceJarFile = reinforceJarFile;
this.reinforceUsername = reinforceUsername;
this.reinforcePassword = reinforcePassword;
this.outputFile = outputFile;
}
public Extension(String appName, String uKey, String apiKey) {
this.uKey = uKey;
this.apiKey = apiKey;
this.appName = appName;
}
public static Extension getConfig(Project project) {
Extension extension = project.getExtensions().findByType(Extension.class);
if (extension == null) {
extension = new Extension();
}
return extension;
}
}
這裏,我們看到,我們只添加了加固jar包的文件,用戶名,密碼,加固後的apk路徑,並沒有添加簽名信息等。這是因爲,這些參數,我們可以通過系統的擴展來獲取到。
添加加固後的apk路徑,是爲了修改後面上傳到蒲公英的時候,上傳的是加固的包。不然的話,上傳的包依然是我們加固前的包。
四,修改上傳Task的apk路徑
之前上傳的Task,是直接獲取的系統打包的路徑。我們要修改成加固後,輸入的apk路徑。
public class PGYUploadTask extends DefaultTask {
private BaseVariant mVariant;
private Project mTargetProject;
public static class PGYRequest {
public String uKey;
public String apiKey;
//1,install by public 2,install by password 3,install by invite
public String installType;
}
public void init(BaseVariant variant, Project project) {
this.mVariant = variant;
this.mTargetProject = project;
setDescription("upload to pgy");
setGroup(TestJavaPlugin.PLUGIN_EXTENSION_NAME);
}
@TaskAction
public void uploadToPGY() {
Extension extension = Extension.getConfig(mTargetProject);
PGYRequest request = new PGYRequest();
request.apiKey = extension.apiKey;
request.uKey = extension.uKey;
File apkDir = extension.outputFile;
if (apkDir == null || !apkDir.exists()) {
upload(request);
} else {
File[] files = apkDir.listFiles();
if (files != null && files.length > 0) {
upload(request.uKey, request.apiKey, files[0]);
}else{
upload(request);
}
}
// for (BaseVariantOutput output : mVariant.getOutputs()) {
// File file = output.getOutputFile();
// if (file == null || !file.exists()) {
// throw new GradleException("apk file is not exist!");
// }
// Extension extension = Extension.getConfig(mTargetProject);
//
// PGYRequest request = new PGYRequest();
// request.apiKey = extension.apiKey;
// request.uKey = extension.uKey;
// upload(request.uKey, request.apiKey, file);
// }
}
private void upload(PGYRequest request) {
for (BaseVariantOutput output : mVariant.getOutputs()) {
File file = output.getOutputFile();
if (file == null || !file.exists()) {
throw new GradleException("apk file is not exist!");
}
upload(request.uKey, request.apiKey, file);
}
}
private void upload(String ukey, String apiKey, File apkFile) {
//builder
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
//add part
bodyBuilder.addFormDataPart("uKey", ukey);
bodyBuilder.addFormDataPart("_api_key", apiKey);
//add file
bodyBuilder.addFormDataPart("file", apkFile.getName(), RequestBody
.create(MediaType.parse("*/*"), apkFile));
//request
Request request = new Request.Builder()
.url("http://upload.pgyer.com/apiv1/app/upload")
.post(bodyBuilder.build())
.build();
OkHttpClient client = new OkHttpClient();
try {
Response response = client.newCall(request).execute();
String result = response.body().string();
System.out.println("upload result: " + result);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
}
}
}
這裏我們根據定義在Extension中的加固後apk路徑,選擇一個apk來上傳(360支持多渠道打包)。
其他內容,有不明白的,可以看下Gradle 自定義Plugin插件之上傳APK到蒲公英
最後,我們在修改下任務的依賴關係就完成了,我們的流程,打包–加固–上傳。
五、修改插件的依賴關係
這裏,我們只需要修改插件裏,任務的依賴關係就可以了。
public class TestJavaPlugin implements Plugin<Project> {
public static final String PLUGIN_EXTENSION_NAME = "uploadHelperJava";
public static final String ANDROID_EXTENSION_NAME = "android";
@Override
public void apply(Project project) {
Extension customExtension = project.getExtensions().create(PLUGIN_EXTENSION_NAME, Extension.class);
// project.getExtensions().create(PLUGIN_EXTENSION_NAME, Extension.class, project);
//項目編譯完成後,回調
project.afterEvaluate(new Action<Project>() {
@Override
public void execute(Project project) {
DomainObjectSet<ApplicationVariant> appVariants = ((AppExtension) project
.getExtensions().findByName(ANDROID_EXTENSION_NAME)).getApplicationVariants();
for (ApplicationVariant variant : appVariants) {
//release apk
if (variant.getBuildType().getName().equalsIgnoreCase("uploadRelease") ) {
String variantName =
variant.getName().substring(0, 1).toUpperCase() + variant.getName().substring(1);
PGYUploadTask uploadTask = project.getTasks()
.create("uploadJavaFor" + variantName, PGYUploadTask.class);
uploadTask.init(variant, project);
ReinforceTask reinforceTask = project.getTasks()
.create("reinforceFor" + variantName, ReinforceTask.class);
reinforceTask.init(variant,project);
//依賴關係
variant.getAssembleProvider().get().dependsOn(project.getTasks().findByName("clean"));
reinforceTask.dependsOn(variant.getAssembleProvider().get());
uploadTask.dependsOn(reinforceTask);
}
}
}
});
}
}
我們讓 加固的Task dependon 打包Task,然後,上傳Task dependon 加固Task。這樣,就完成了我們整個的流程。 clean—打包—加固—上傳。
六、項目調用
這裏,主要有發佈插件,把加固包copy到項目中,然後,引用插件,填寫參數,調用。
1,首先,我們重新發佈下,我們的插件
點擊,alone_plugin下面的uploadArchives。
2,要使用的項目,引入360加固保
我們把加固保的解壓文件都copy的項目的libs文件夾下(文件名隨意,只要在配置reinforceJarFile時,指向它就可以)
這裏,我們的加固命令,都是通過jiagu.jar來完成的。
參數命令都在help.txt裏面,這裏就不介紹了
3,項目下的build.gradle引入插件
測試項目引入,我們的加固上傳插件
buildscript {
repositories {
google()
jcenter()
maven{
url uri('./repo/')
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
classpath 'com.liu.alone.plugin:java-plugin:1.0.0'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
4,app的module引入插件
app的build.gradle
android {
...
signingConfigs {
release {
storeFile file('xxx.jks')
storePassword "xxx"
keyAlias "xxx"
keyPassword "xxx"
}
}
buildTypes {
release {
minifyEnabled false
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
uploadRelease {
minifyEnabled false
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
...
apply plugin: 'com.liu.alone.plugin'
uploadHelperJava {
appName = "testGradle"
uKey = "c9d2625c0cf221d8f4a98738f4c05e9a"
apiKey = "fac8875534d045a2be3f229abd46cc3e"
//加固相關的信息
reinforceJarFile = file("../libs/jiagu.jar")
reinforceUsername = "xxx"
reinforcePassword = "xxx"
outputFile = file("${buildDir.getAbsolutePath()}\\jiagu")
}
引用我們寫的插件,及配置上傳加固等等需要的參數信息
reinforceJarFile 這個加固文件就是指向了libs下的jiagu.jar。如果,加固保放到其他的目錄下,修改這裏就可以了。
5,最後調用任務
執行Gradle裏面Tasks任務的uploadJavaForUploadRelease,我們定義的這個任務。
打印結果
任務完成_已簽名
> Task :app:uploadJavaForUploadRelease
upload result: {"code":0,"message":"","data":{"appKey":"bb2b8b14f405d9024b0e18954beaadb8","userKey":"c9d2625c0cf221d8f4a98738f4c05e9a","appType":"2","appIsLastest":"2","appFileSize":"2206305","appName":"ObjectAnimatorTest","appVersion":"1.0","appVersionNo":"1","appBuildVersion":"9","appIdentifier":"com.liu.objectanimatortest","appIcon":"825df8e761ed960f5c3237e9af332df7","appDescription":"","appUpdateDescription":"","appScreenshots":"","appShortcutUrl":"t6rG","appCreated":"2020-01-02 11:19:53","appUpdated":"2020-01-02 11:19:53","appQRCodeURL":"http:\/\/www.pgyer.com\/app\/qrcodeHistory\/5dea8789700159c1d2804374dc80293b2e3f5e5ffd53531b777cfc4a33843c29"}}
打印日誌,看到先完成簽名,然後,執行了上傳任務,最後,返回上傳結果
其他
1,插件的build.gradle
apply plugin: 'java-library'
apply plugin: 'maven'
dependencies {
compile gradleApi()
compile localGroovy()
compile 'com.android.tools.build:gradle:3.3.1'
implementation("com.squareup.okhttp3:okhttp:3.8.1")
}
repositories {
mavenCentral()
}
group = 'com.liu.alone.plugin'
version = '1.0.0'
archivesBaseName = 'java-plugin'
//upload
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('../repo'))
}
}
}
主要有java的依賴庫,及okhttp的上傳庫,插件發佈的內容。
2,插件的整體結構
主要就是build.gradle,properties文件,一個插件類,2個任務類,一個擴展屬性類。
到這裏,我們的流程:打包–加固–上傳就完成。
如果,我們上傳前,不一定需要加固呢,我們是不是可以在Extension裏面定義一個變量來控制呢,只需要修改插件的依賴關係,上傳Task的apk上傳路徑即可。