前回の記事ではGradleプラグインの使い方、apk強化、Dandelionへのアップロード方法を紹介しました。
この記事は主に、Gradle プラグインを使用してプロセスをさらに改善し、パッケージ化 - 強化 - タンポポのアップロード - DingTalk メッセージの送信を実現し、完全な自動化を実現することを目的としています。
前回の記事の紹介:
Gradle カスタム プラグイン APK を Dandelion にアップロード
次に、DingTalk 通知を送信する簡単なプロセスを見てみましょう。
- DingTalk ロボットを作成します。メッセージを送信するための資格情報を取得します。
- 認証情報とDingTalkで必要なデータ形式に従ってAPIを呼び出します。
DingTalk の API ドキュメントには詳細なプロセスの説明が含まれているため、ここでは詳しく説明しません。
DingTalk メッセージ タスクの開発タスク フロー:
- 1. DingTalk ロボットを作成する
- 2. DingTalk メッセージを送信するための Task タスクを作成する
- 3. Dandelion Task タスクを変更してアップロードします
- 4 番目に、Plugin プラグイン内のタスクの依存関係を変更します。
1. DingTalk ロボットを作成する
DingTalk メッセージを送信するには、DingTalk グループに DingTalk ボットを作成する必要があります。DingTalk ロボットを作成したら、ロボットの Webhook とシークレットを介して DingTalk グループにメッセージを送信します。
1.「グループスマートグループアシスタント」をクリックします。
2. 「ロボットの作成」をクリックします。
クリックしてロボットを追加します (許可が必要な場合があります)。これにはコンピューターの操作が必要です。ロボットの作成後、Webhook とシークレットを取得します。
これら 2 つのパラメータは、データを送信するために必要なものです。
ロボットを作成した後、DingTalk メッセージを送信するための資格情報である Webhook とシークレットを取得しました。
ロボットを作成した後、DingTalk の API ドキュメントに従って資格情報を介して API を呼び出すことができます。ここでは公式の SDK を使用せず、ドキュメントの形式に従って OKHttp 経由でメッセージを送信します。
2. DingTalk メッセージを送信するための Task タスクを作成する
ドキュメントによると、DingTalk を送信するには、パラメータwebhook、secretを渡す必要があることがわかっています。
次に、送信するメッセージのタイプに応じてさまざまなパラメーターを選択します。ここでは、リンク タイプを使用してメッセージを送信します。
{
"msgtype": "link",
"link": {
"text": "这个即将发布的新版本,创始人xx称它为“红树林”。
而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是“红树林”?",
"title": "时代的火车向前开",
"picUrl": "",
"messageUrl": "https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI"
}
}
ここでは、text、title、picUrl、messageUrlをアップロードする必要があることがわかります。4つのパラメータ
picUrl、link messageUrl は、Dandelion にアップロードした後に取得する必要があります。
この場合、Webhook、secret、text、titleの 4 つのパラメーターを追加する拡張機能が必要です。他の 2 つのパラメーター (picUrl、messageUrl) は、Dandelion を正常にアップロードした後に取得する必要があります。
上記のパラメータの準備ができたら、OKHttp を介して API を呼び出すだけで完了します。
準備には次のものが含まれます。
- DingTalk のニーズを満たす Model クラスを構築する
- 拡張機能にパラメーターを追加します (上記で分析した 4 つのパラメーター)
- Dandelion のアップロードに成功したら、Url パラメータを取得します
- 拡張機能のパラメータの解析
- API を呼び出して DingTalk メッセージを送信する
1. DingTalk のニーズを満たす Model クラスを構築する
DingTalk の送信は Json を通じて配信されます。最初に必要なモデルを構築します
public static class DingTalkModel {
public String picUrl;
public String messageUrl;
public String title;
public String text;
}
public static class DingTalkRequest {
public String msgtype = "link";
public DingTalkModel link;
public DingTalkRequest(DingTalkModel link) {
this.link = link;
}
}
2. 拡張機能にパラメータを追加する
ここには主に 4 つのパラメータがあります: アップロード認証情報 (Webhook とシークレット)、送信メッセージ パラメータ (タイトル、コンテンツ)。
Extensionクラスにパラメータを追加します。
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;
//dingding
public String dingWebhook;
public String dingSecret;
public String title;
public String content;
public Extension() {
}
public Extension(String uKey, String apiKey, String appName, File reinforceJarFile,
String reinforceUsername, String reinforcePassword, File outputFile,
String dingWebhook, String dingSecret, String title, String content) {
this.uKey = uKey;
this.apiKey = apiKey;
this.appName = appName;
this.reinforceJarFile = reinforceJarFile;
this.reinforceUsername = reinforceUsername;
this.reinforcePassword = reinforcePassword;
this.outputFile = outputFile;
this.dingWebhook = dingWebhook;
this.dingSecret = dingSecret;
this.title = title;
this.content = content;
}
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;
}
}
3. Dandelion のアップロードが成功したら、Url パラメータを取得します
DingTalk タスクで静的プロパティを定義します。Dandelion アップロード タスクが完了したら、setData() メソッドを呼び出してパラメータを渡します。
public class DingTalkTask extends DefaultTask {
private static String sShotcutUrl, sQRCodeURL;
...
public static void setData(String shortcutUrl, String qrUrl) {
sShotcutUrl = shortcutUrl;
sQRCodeURL = qrUrl;
}
}
4. 拡張機能のパラメータを解析する
ここではネットワーク化前のパラメータの準備ですが、ここで必要なパラメータを解析してモデル クラスに入れる必要があります。
private DingTalkModel initModel() {
DingTalkModel model = new DingTalkModel();
model.picUrl = sQRCodeURL;
model.messageUrl = "http://www.pgyer.com/" + sShotcutUrl;
Extension extension =Extension.getConfig(mTargetProject);
model.title = extension.title;
model.text = extension.content;
mWebhook = extension.dingWebhook;
mSecret = extension.dingSecret;
return model;
}
ここで DingTalk に必要なモデルを作成し、対応するすべてのパラメーターを取得します。
5. APIを呼び出してDingTalkメッセージを送信します
これは、OKHttp を使用して直接呼び出します
@TaskAction
public void sendDingtalk() {
DingTalkModel model = initModel();
String url = createDingUrl();
OkHttpClient okHttpClient = new OkHttpClient();
Gson gson = new Gson();
String json = gson.toJson(new DingTalkRequest(model));
RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8")
, json);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
System.out.println("send message result: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* create dingtalk url
*/
private String createDingUrl() {
Long timestamp = System.currentTimeMillis();
return mWebhook + "×tamp=" + timestamp + "&sign=" + createSign(timestamp, mSecret);
}
private String createSign(Long timestamp, String secret) {
String stringToSign = timestamp + "\n" + secret;
Mac mac = null;
try {
mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
return URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
この時点で、DingTalk タスクが作成されます。完全なコードを投稿してください。
public class DingTalkTask extends DefaultTask {
private static String sShotcutUrl, sQRCodeURL;
private BaseVariant mVariant;
private Project mTargetProject;
private String mWebhook;
private String mSecret;
public static class DingTalkModel {
public String picUrl;
public String messageUrl;
public String title;
public String text;
}
public static class DingTalkRequest {
public String msgtype = "link";
public DingTalkModel link;
public DingTalkRequest(DingTalkModel link) {
this.link = link;
}
}
public void init(BaseVariant variant, Project project) {
this.mVariant = variant;
this.mTargetProject = project;
setDescription("send message to ding talk");
setGroup(TestJavaPlugin.PLUGIN_EXTENSION_NAME);
}
@TaskAction
public void sendDingtalk() {
DingTalkModel model = initModel();
String url = createDingUrl();
OkHttpClient okHttpClient = new OkHttpClient();
Gson gson = new Gson();
String json = gson.toJson(new DingTalkRequest(model));
RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8")
, json);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
System.out.println("send message result: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* create dingtalk url
*/
private String createDingUrl() {
Long timestamp = System.currentTimeMillis();
return mWebhook + "×tamp=" + timestamp + "&sign=" + createSign(timestamp, mSecret);
}
private DingTalkModel initModel() {
DingTalkModel model = new DingTalkModel();
model.picUrl = sQRCodeURL;
model.messageUrl = "http://www.pgyer.com/" + sShotcutUrl;
Extension extension =Extension.getConfig(mTargetProject);
model.title = extension.title;
model.text = extension.content;
mWebhook = extension.dingWebhook;
mSecret = extension.dingSecret;
return model;
}
private String createSign(Long timestamp, String secret) {
String stringToSign = timestamp + "\n" + secret;
Mac mac = null;
try {
mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
return URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
public static void setData(String shortcutUrl, String qrUrl) {
sShotcutUrl = shortcutUrl;
sQRCodeURL = qrUrl;
}
}
3. Dandelion Task タスクを変更してアップロードします
上で述べたように、アップロードが完了したら、URL を DingTalk タスク タスクに渡す必要があります。
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);
}
}
}
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);
parseResult(result);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private void parseResult(String result) {
if (result == null || result.length() == 0) {
throw new IllegalArgumentException("upload apk to PGY failed");
}
PGYResonpse data = new Gson().fromJson(result, PGYResonpse.class);
if (data == null || data.code != 0) {
throw new IllegalArgumentException("upload apk to PGY failed");
}
DingTalkTask.setData(data.data.appShortcutUrl, data.data.appQRCodeURL);
}
public static class PGYResonpse {
public int code;
public String message;
public PGYDetail data;
public static class PGYDetail {
public String appShortcutUrl;
public String appUpdated;
public String appQRCodeURL;
public String appVersion;
public String appVersionNo;
public String appIcon;
}
}
}
ここでは、まず Dandelion によって返される結果の Model クラスを作成し、アップロードされた Dandelion 結果を取得した後、DingTalkTask.setData() メソッドを通じてデータを DingTalk Task タスクに渡します。
4 番目に、Plugin プラグイン内のタスクの依存関係を変更します。
ここでは主に、Plugin プラグイン内のタスクの依存関係を変更します。DingTalk のタスクを Dandelion のアップロード タスクに依存させ、アップロードが成功した後に DingTalk 通知が送信されるようにします。
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) {
project.getExtensions()
.create(PLUGIN_EXTENSION_NAME, Extension.class);
//项目编译完成后,回调
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);
//create task
ReinforceTask reinforceTask = project.getTasks()
.create("reinforceFor" + variantName, ReinforceTask.class);
reinforceTask.init(variant, project);
PGYUploadTask uploadTask = project.getTasks()
.create("uploadJavaFor" + variantName, PGYUploadTask.class);
uploadTask.init(variant, project);
DingTalkTask talkTask = project.getTasks()
.create("dingTalkFor" + variantName, DingTalkTask.class);
talkTask.init(variant, project);
//depend on
variant.getAssembleProvider().get()
.dependsOn(project.getTasks().findByName("clean"));
reinforceTask.dependsOn(variant.getAssembleProvider().get());
uploadTask.dependsOn(reinforceTask);
talkTask.dependsOn(uploadTask);
}
}
}
});
}
}
ここでは、DingTalk タスクも作成しました。その後、依存関係が変更されました。この時点で、パッケージ化 – 強化 – Dandelion のアップロード – DingTalk 通知の送信というプロセス全体が完了しました。
5、電話してください
プラグインのリリースや導入については以前の記事(冒頭にリンクあり)で触れているのでここでは書きません。
プラグインを呼び出す必要があるアプリのモジュールの下にある build.gradle を見てみましょう。
apply plugin: 'com.android.application'
android {
......
signingConfigs {
release {
storeFile file('xxx.jks')
storePassword "xxx"
keyAlias "xxx"
keyPassword "xxx"
}
}
buildTypes {
release {
minifyEnabled false
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 = "测试APP"
uKey = "xxxx"
apiKey = "xxxx"
//加固相关的信息
reinforceJarFile = file("../libs/jiagu.jar")
reinforceUsername = "18032793516"
reinforcePassword = "san7758521"
outputFile = file("${buildDir.getAbsolutePath()}\\jiagu")
//钉钉,这里需要修改成自己的值
dingWebhook = "https://oapi.dingtalk.com/robot/send?access_token=xxx"
dingSecret= "xxx"
title = "新版本APP上传完成"
content="体验新版本APP,请到蒲公英下载"
}
タスク呼び出し:
印刷結果:
> Task :app:reinforceForUploadRelease
==============start JiaGu
....
任务完成_已签名
> Task :app:uploadJavaForUploadRelease
upload result: {"code":0,"message":"","data":{"appKey":"3e95fe0e7fa74e9f7a310bbdfbd139cc","userKey":"c9d2625c0cf221d8f4a98738f4c05e9a","appType":"2","appIsLastest":"2","appFileSize":"3264349","appName":"PluginTest","appVersion":"1.0","appVersionNo":"1","appBuildVersion":"4","appIdentifier":"com.liu.plugintest","appIcon":"9664daaa8a725f726bcb6018495f14a7","appDescription":"","appUpdateDescription":"","appScreenshots":"","appShortcutUrl":"DqmH","appCreated":"2020-01-03 12:21:24","appUpdated":"2020-01-03 12:21:24","appQRCodeURL":"http:\/\/www.pgyer.com\/app\/qrcodeHistory\/9c27fd62288ce774fafb622d8f6df0bbcd9f90cf0b9ba39846c1c6864c93b135"}}
> Task :app:dingTalkForUploadRelease
send message result: {"errcode":0,"errmsg":"ok"}
ここでは、3 つのタスクが連続して実行されたことがわかります: Task :app:reinforceForUploadRelease、Task :app:uploadJavaForUploadRelease、Task :app:dingTalkForUploadRelease
最後にDingTalk通知
この時点で、リリースのテストの自動化プロセス全体 (パッケージ化、強化、Dandelion のアップロード、DingTalk 通知の送信) は、 Gradle プラグインをカスタマイズすることによって実現されます。
その他の書類
プラグイン-ビルド.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'))
}
}
}