Gradle 自定义Plugin插件之发送钉钉通知

在之前的文章中,我们介绍了怎么使用Gradle插件,apk加固,上传到蒲公英。

这篇文章,主要就是把流程进一步完善,通过Gradle插件实现:打包—加固—上传蒲公英—发送钉钉消息,实现完全自动化.。

之前的文章介绍:

Gradle 自定义Plugin插件介绍

Gradle 自定义Plugin插件之上传APK到蒲公英

Gradle 自定义Plugin插件之360加固

下面,我们看下发送钉钉通知的简单流程:

  • 创建钉钉机器人。获取发送消息的凭证。
  • 根据根据凭证和钉钉的需要的数据格式,来调用API

钉钉的API文档 里面有详细的流程描述,这里就不说了。

开发钉钉消息Task任务流程:

一、创建钉钉机器人

要发送钉钉消息,我们需要在钉钉群里创建一个钉钉机器人。在创建钉钉机器人后,通过这个机器人的Webhook和secret实现发消息到钉钉群。

1,点击群的智能群助手

在这里插入图片描述

2,点击创建机器人

点击添加机器人(可能需要权限),这个需要电脑操作,在创建机器人后,会获取到Webhook和secret。
这两个参数就是我们发送数据需要的。

在这里插入图片描述
创建完机器人后,我们就拿到了webhook和secret,这是发送钉钉消息的凭证。
在这里插入图片描述

创建完机器人后,我们根据钉钉的API文档,通过凭证,就可以调用API了。这里我没有使用官方的SDK,是根据文档的格式,通过OKHttp来发送消息的。

二,创建发送钉钉消息的Task任务

根据文档,我们知道,发送钉钉,我们必须要传的参数:webhook,secret
然后,根据我们发送消息的类型选择不同的参数,我们这里使用link类型来发送消息:

{
    "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,四个参数
picUrl,链接messageUrl,肯定是我们上传到蒲公英之后才能获取的。

这样的话,我们需要Extension来增加4个参数:webhook,secret,text,title。另外两个参数(picUrl,messageUrl)需要等到上传蒲公英成功后获取。

等上面的参数准备好了以后,我们只要通过OKHttp调用API就可以完成了。

准备工作有:

  • 构建符合钉钉需要的Model类
  • 给Extension添加参数(上面分析的四个参数)
  • 获取上传蒲公英成功后的Url参数
  • 解析Extension的参数
  • 调用API发送钉钉消息

1,构建符合钉钉需要的Model类

发送钉钉是通过Json方式来传递的,我们先构建它需要的model

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,给Extension添加参数

这里的参数主要有4个:上传凭证(webhook和secret),发送消息参数(title,content)。

在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,获取上传蒲公英成功后的Url参数

我们在钉钉任务中定义静态属性,当蒲公英上传任务完成后,调用setData()方法,就可以把参数传递过来。

public class DingTalkTask extends DefaultTask {

    private static String sShotcutUrl, sQRCodeURL;
	...

    public static void setData(String shortcutUrl, String qrUrl) {
        sShotcutUrl = shortcutUrl;
        sQRCodeURL = qrUrl;
    }
}

4,解析Extension的参数

这里就是联网前的参数准备了,我们要把需要的参数都在这里解析出来,放到model类里

   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;
    }

这里创建钉钉需要的model,然后把相应的参数都获取到。

5,调用API发送钉钉消息

这个,直接使用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 + "&timestamp=" + 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 "";
    }


到这里,我们的钉钉Task就创建完成了。贴一下完整的代码。


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 + "&timestamp=" + 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;
    }
}

三、修改上传蒲公英Task任务

上面说了,我们在上传完成后,需要把URL传递给钉钉的Task任务。

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;

        }
    }

}

这里,首先创建蒲公英返回结果的Model类;然后,在获取到上传蒲公英结果后,就通过DingTalkTask.setData()方法,把数据传递给钉钉的Task任务。

四,修改Plugin插件里任务的依赖关系

这里,主要是修改下Plugin插件里的任务依赖关系。让钉钉的Task 依赖 上传蒲公英的Task,以便,在上传成功后,发送钉钉通知。

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);

                    }
                }
            }
        });
    }
}

这里,我们把钉钉Task也创建出来了。然后,修改了依赖关系。到这里,我们的整个流程:打包–加固–上传蒲公英–发送钉钉通知,就完成了。

五,调用

发布插件,和引入插件,之前的文章都有说(开头有链接),这里就不写了。

我们看下需要调用插件的app这个module下的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"}

这里,我们看到了三个任务已经先后执行完成:Task :app:reinforceForUploadRelease,Task :app:uploadJavaForUploadRelease,Task :app:dingTalkForUploadRelease

最后,钉钉通知
在这里插入图片描述

到这里,我们整个的测试发布的自动化流程:打包—加固—上传蒲公英—发送钉钉通知,就通过自定义Gradle插件实现了。

其它文件

插件的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'))
        }
    }
}

猜你喜欢

转载自blog.csdn.net/ecliujianbo/article/details/103818432