0%

Gradle插件自定义


国外的疫情越来越严重,不排除有卷土重来的可能,今年突如其来的疫情影响着每一个人,就业形势也不容乐观,不管是企业还是个人都要共同面对这次生存考验,今年的毕业生也是非常的不容易,不管外界如何变化,持续强化自己的专业技能永远是立身之本,提升自己的核心竞争力永远是生存之道。最近学了一些自定义gradle插件相关的知识,开发了自动上传fir并发送钉钉消息的gradle插件,提升了个人和团队的工作效率,在此记录并分享。


由于公司当前的开发流程尚未用到Jenkins这类在线的自动化构建平台,开发流程仍然是开发者本地打包再上传fir托管平台并发送上传完成的消息到钉钉群里,再@相关的测试人员提醒可进行测试,这一系列流程化的操作显得过于繁琐。作为Android开发掌握groovy这门语言也是有必要的,毕竟这是Android studio的构建脚本语言且语法与java相似,完全可以利用构建脚本实现打包后自动上传到fir托管平台,再利用自定义钉钉机器人的官方api发送钉钉消息给相关的测试人员,解放双手,自动化这一系列的流程操作来提升工作效率,减少加班的可能,打包/上传/发消息这段时间喝杯咖啡放松一下不是更好吗?我决定把这个插件打包上传到JitPack分享给团队和网络上的其他人,因为帮更多的人提升工作效率确实是一件很cool的事情

实现自定义Gradle插件的三种方式

app的gradle文件中定义

  1. 直接修改app的gradle文件,继承gradle的Plugin类,定义一个自定义插件的实现类,重写apply方法完成插件逻辑
    1
    2
    3
    4
    5
    6
    7
    apply plugin: PluginImpl
    class PluginImpl implements Plugin<Project>{
    @override
    void apply(Project project){
    println("hello Plugin")
    }
    }
    优点:方便调试
    缺点:跟app工程代码混在一起,无法给其它项目使用

    buildSrc目录定义

    在工程目录下,新建buildSrc目录,在该目录下开发
    新建文件夹src/main/groovy/xxx/xxx/xxx,其中xxx/xxx/xxx是包名,在这个目录下自定义gradle插件类PluginImpl
    新建文件夹resource/META-INF/gradle-plugins,在这个目录下新建文件xxx.xxx.xxx.properties,声明插件实现类implementation-class=xxx.xxx.xxx.PluginImpl
    效果图
    优点:方便调试,与app工程分离
    缺点:无法给其它项目使用

新增插件lib定义

新建一个gradle插件的lib库,删除其它的所有文件,保持项目结构与buildSrc目录一样,在这个lib库下开发
优点:与app工程分离,可打包上传maven,在其它项目中引用并分享给其他人
缺点:调试稍微麻烦,需发布到本地仓库
以上三种方式最好的是第三种,与app工程分离,可发布到本地maven仓库下调试,也可以打包插件上传到私有的maven仓库或者公有maven仓库如JitPack分享给其他人

定义插件参数

定义fir配置参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.demo.plugin

import javax.inject.Inject

public class FirExtension {
private String appName //app名称
private String iconPath //图标路径
private String token //fir设置中的上传token

@Inject
public FirExtension() {
}

String getAppName() {
return appName
}

void setAppName(String appName) {
this.appName = appName
}

String getIconPath() {
return iconPath
}

void setIconPath(String iconPath) {
this.iconPath = iconPath
}

String getToken() {
return token
}

void setToken(String token) {
this.token = token
}


@Override
public String toString() {
return "FirExtension{" +
"appName='" + appName + '\'' +
", iconPath='" + iconPath + '\'' +
", token='" + token + '\'' +
'}';
}
}

定义钉钉配置参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.demo.plugin

import javax.inject.Inject

public class DingTalkExtension {
private String webHook // 钉钉自定义机器人的webhook
private String title // 消息标题
private String content // 内容
private boolean isAtAll // 是否@所有人
private List<String> atMobiles // 手机号列表,单独@某些人
@Inject
public DingTalkExtension() {
}

String getWebHook() {
return webHook
}

void setWebHook(String webHook) {
this.webHook = webHook
}

String getTitle() {
return title
}

void setTitle(String title) {
this.title = title
}

String getContent() {
return content
}

void setContent(String content) {
this.content = content
}

boolean getIsAtAll() {
return isAtAll
}

void setIsAtAll(boolean isAtAll) {
this.isAtAll = isAtAll
}

List<String> getAtMobiles() {
return atMobiles
}

void setAtMobiles(List<String> atMobiles) {
this.atMobiles = atMobiles
}


@Override
public String toString() {
return "DingTalkExtension{" +
"webHook='" + webHook + '\'' +
", title='" + title + '\'' +
", content='" + content + '\'' +
", isAtAll=" + isAtAll +
", atMobiles=" + atMobiles +
'}';
}
}

定义插件参数

  • 结合上面两类参数配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package com.demo.plugin

    import org.gradle.api.Action
    import org.gradle.api.model.ObjectFactory

    class UploadApkPluginExtension {
    FirExtension firExtension
    DingTalkExtension dingTalkExtension
    public UploadApkPluginExtension(ObjectFactory objectFactory) {
    firExtension = objectFactory.newInstance(FirExtension.class)
    dingTalkExtension = objectFactory.newInstance(DingTalkExtension.class)
    }

    public void fir(Action<FirExtension> action) {
    action.execute(firExtension)
    }

    public void dingTalk(Action<DingTalkExtension> action){
    action.execute(dingTalkExtension)
    }

    }

    插件逻辑

    groovy中我们仍然可以使用okhttp,Gson这类Android中常用的开源框架来实现网络请求,Json序列化/反序列化相关的逻辑,封装网络请求工具类,最后在自定义Plugin中调用这个工具类完成自动化

    引入开源库

    修改插件lib中的gradle文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    apply plugin: 'java-library'
    apply plugin: 'groovy'
    apply plugin: 'maven'
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation gradleApi()//gradle sdk
    implementation localGroovy()//groovy sdk
    implementation 'com.squareup.okhttp3:okhttp:4.7.2'
    implementation 'com.google.code.gson:gson:2.8.6'
    }

    repositories {
    mavenCentral()
    jcenter()
    }

    网络请求工具类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    package com.demo.plugin

    import com.google.gson.Gson
    import okhttp3.*

    import java.util.concurrent.TimeUnit

    public class OkHttpUtil{
    OkHttpClient okHttpClient
    Gson gson
    DingTalk dingTalk // 序列化钉钉消息工具类
    public OkHttpUtil(){
    okHttpClient = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(60, TimeUnit.SECONDS).build()
    gson = new Gson()
    dingTalk = new DingTalk()
    }
    // 获取fir上传证书
    BundleApp getCert(String appPackage, String apiTokenFir){

    FormBody.Builder build = new FormBody.Builder()
    build.add("bundle_id", appPackage)
    build.add("api_token", apiTokenFir)
    build.add("type", "android")
    Request request = new Request.Builder().url("http://api.bq04.com/apps").post(build.build()).build()
    Response response = okHttpClient.newCall(request).execute()
    String result = response.body.string()
    return gson.fromJson(result, BundleApp.class)
    }

    // 上传apk到fir
    String uploadApk(String apkPath,String key,String token,String appName,String appVersion,String appBuild,String fileName,String upload_url) {
    RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), new File(apkPath))
    MultipartBody body = new MultipartBody.Builder()
    .setType(MediaType.parse("multipart/form-data"))
    .addFormDataPart("key", key)
    .addFormDataPart("token", token)
    .addFormDataPart("x:name", appName)
    .addFormDataPart("x:version", appVersion)
    .addFormDataPart("x:build", appBuild)
    .addFormDataPart("file", fileName, fileBody)
    .build()
    Request requestApk = new Request.Builder().url(upload_url).post(body).build()

    Response responseApk = okHttpClient.newCall(requestApk).execute()
    return responseApk.body.string()
    }

    // 上传icon到fir
    String uploadIcon(String apkIconPath,String keyIcon,String tokenIcon,String upload_urlIcon){
    RequestBody fileBodyIcon = RequestBody.create(MediaType.parse("application/octet-stream"),new File(apkIconPath))
    MultipartBody bodyIcon = new MultipartBody.Builder()
    .setType(MediaType.parse("multipart/form-data"))
    .addFormDataPart("key", keyIcon)
    .addFormDataPart("token", tokenIcon)
    .addFormDataPart("file", "icon.png", fileBodyIcon)
    .build()
    Request requestIcon = new Request.Builder().url(upload_urlIcon).post(bodyIcon).build()
    Response responseIcon = okHttpClient.newCall(requestIcon).execute()
    return responseIcon.body.string()
    }

    ApkInfo getApkUrl(String appPackage, String apiTokenFir) {
    // 获取成功连接
    String queryurl =
    "http://api.bq04.com/apps/latest/$appPackage?api_token=$apiTokenFir&type=android"
    Request requestUrl = new Request.Builder().url(queryurl).get().build()
    Response responseUrl = okHttpClient.newCall(requestUrl).execute()
    String result = responseUrl.body.string()
    return gson.fromJson(result,ApkInfo.class)
    }

    // 发送钉钉链接消息
    void sendDingTalkLink(String text,String title,String url,String webHook){
    RequestBody linkBody = FormBody.create(MediaType.parse("application/json; charset=utf-8")
    , dingTalk.createLinkMsg(text,title,url))
    Request linkDingTalk = new Request.Builder().url(webHook)
    .post(linkBody).build()
    Response responseLink = okHttpClient.newCall(linkDingTalk).execute()
    String result = responseLink.body.string()
    println("已发送钉钉链接:$result")
    }

    // 发送钉钉文本消息
    void sendDingTalkMsg(String text,String webHook,boolean isAtAll,List<String> atMobiles){
    RequestBody textBody = FormBody.create(MediaType.parse("application/json; charset=utf-8")
    , dingTalk.createTextMsg(text,atMobiles,isAtAll))
    Request textDingTalk = new Request.Builder().url(webHook)
    .post(textBody).build()
    Response responseText = okHttpClient.newCall(textDingTalk).execute()
    String result = responseText.body.string()
    println("已发送钉钉消息:$result")
    }
    }

    序列化工具类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    package com.demo.plugin
    import com.google.gson.Gson

    public class DingTalk {
    Gson gson
    public DingTalk(){
    gson = new Gson()
    }
    // 定义钉钉链接消息的bean类
    def class LinkMsg{
    String msgtype
    Link link

    public LinkMsg(String msgtype,Link link){
    this.msgtype = msgtype
    this.link = link
    }
    }

    def class Link{
    String title
    String text
    String messageUrl

    public Link(String title,String text,String messageUrl){
    this.title = title
    this.text = text
    this.messageUrl = messageUrl
    }
    }

    // 定义钉钉文本消息的bean类
    def class TextMsg{
    String msgtype
    Text text
    At at
    public TextMsg(String msgtype,Text text,At at){
    this.msgtype = msgtype
    this.text = text
    this.at = at
    }
    }

    def class At{
    List<String> atMobiles = new ArrayList<>()
    boolean isAtAll = true
    public At(List<String> atMobiles,boolean isAtAll){
    this.atMobiles = atMobiles
    if (!atMobiles.isEmpty()){
    this.isAtAll = false
    }
    else{
    this.isAtAll = isAtAll
    }
    }
    }

    def class Text{
    String content

    public Text(String content){
    this.content = content
    }
    }

    /**
    * 构建一个钉钉链接消息
    * @param text String
    * @param title String
    * @param url String
    * @return String
    */
    String createLinkMsg(String text ,String title,String url) {
    Link link = new Link(title,text,url)
    LinkMsg linkMsg = new LinkMsg("link",link)
    return gson.toJson(linkMsg)
    }

    /**
    * 构建一个钉钉文本消息
    * @param msgtype String
    * @param content String
    * @param text String
    * @return String
    */
    String createTextMsg(String content,List<String> atMobiles,boolean isAtAll){
    Text text = new Text(content)
    At at = new At(atMobiles,isAtAll)
    TextMsg textMsg = new TextMsg("text", text, at )
    return gson.toJson(textMsg)
    }
    }

反序列化bean类定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.demo.plugin;

public class ApkInfo {
/**
* name : fir.im
* version : 1.0
* changelog : 更新日志
* versionShort : 1.0.5
* build : 6
* installUrl : http://download.bq04.com/v2/app/install/xxxxxxxxxxxxxxxxxxxx?download_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxx
* install_url : http://download.bq04.com/v2/app/install/xxxxxxxxxxxxxxxx?download_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxx
* update_url : http://fir.im/fir
* binary : {"fsize":6446245}
*/
private String name;
private String version;
private String changelog;
private String versionShort;
private String build;
private String installUrl;
private String install_url;
private String update_url;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getVersion() {
return version;
}

public void setVersion(String version) {
this.version = version;
}

public String getChangelog() {
return changelog;
}

public void setChangelog(String changelog) {
this.changelog = changelog;
}

public String getVersionShort() {
return versionShort;
}

public void setVersionShort(String versionShort) {
this.versionShort = versionShort;
}

public String getBuild() {
return build;
}

public void setBuild(String build) {
this.build = build;
}

public String getInstallUrl() {
return installUrl;
}

public void setInstallUrl(String installUrl) {
this.installUrl = installUrl;
}

public String getInstall_url() {
return install_url;
}

public void setInstall_url(String install_url) {
this.install_url = install_url;
}

public String getUpdate_url() {
return update_url;
}

public void setUpdate_url(String update_url) {
this.update_url = update_url;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package com.demo.plugin;

public class BundleApp {

/**
* id : 5592ceb6537069f2a8000000
* type : ios
* short : yk37
* cert : {"icon":{"key":"xxxxx","token":"xxxxxx","upload_url":"http://upload.qiniu.com"},"binary":{"key":"xxxxx","token":"xxxxxx","upload_url":"http://upload.qiniu.com"}}
*/


private CertBean cert;

public CertBean getCert() {
return cert;
}

public void setCert(CertBean cert) {
this.cert = cert;
}

public static class CertBean {
/**
* icon : {"key":"xxxxx","token":"xxxxxx","upload_url":"http://upload.qiniu.com"}
* binary : {"key":"xxxxx","token":"xxxxxx","upload_url":"http://upload.qiniu.com"}
*/

private IconBean icon;
private BinaryBean binary;

public IconBean getIcon() {
return icon;
}

public void setIcon(IconBean icon) {
this.icon = icon;
}

public BinaryBean getBinary() {
return binary;
}

public void setBinary(BinaryBean binary) {
this.binary = binary;
}

public static class IconBean {
/**
* key : xxxxx
* token : xxxxxx
* upload_url : http://upload.qiniu.com
*/

private String key;
private String token;
private String upload_url;

public String getKey() {
return key;
}

public void setKey(String key) {
this.key = key;
}

public String getToken() {
return token;
}

public void setToken(String token) {
this.token = token;
}

public String getUpload_url() {
return upload_url;
}

public void setUpload_url(String upload_url) {
this.upload_url = upload_url;
}
}

public static class BinaryBean {
/**
* key : xxxxx
* token : xxxxxx
* upload_url : http://upload.qiniu.com
*/

private String key;
private String token;
private String upload_url;

public String getKey() {
return key;
}

public void setKey(String key) {
this.key = key;
}

public String getToken() {
return token;
}

public void setToken(String token) {
this.token = token;
}

public String getUpload_url() {
return upload_url;
}

public void setUpload_url(String upload_url) {
this.upload_url = upload_url;
}
}
}
}

自定义Plugin

  1. 继承gradle的Plugin类,重写apply方法中实现插件逻辑
  2. 在assemble任务后执行插件相关的逻辑
  • 兼容Debug/Release两种打包方式和各个flavor下的打包任务,在这些任务打包完成后执行插件逻辑,采用”assemble” + variant.name.capitalize() +”Fir”的打包命令灵活配置
  • 如渠道名为googlePlay下打debug/release包则打包任务名为assembleGooglePlayDebug/assembleGooglePlayRelease
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    package com.demo.plugin
    import org.gradle.api.Plugin
    import org.gradle.api.Project
    import org.gradle.api.Task

    class PluginImpl implements Plugin<Project> {
    UploadApkPluginExtension extension

    @Override
    void apply(Project project) {
    extension = project.extensions.create('uploadApk', UploadApkPluginExtension.class, project.getObjects())
    if (project.android.hasProperty("applicationVariants")) {
    project.android.applicationVariants.all { variant ->
    // 定义gradle任务名称
    Task uploadFir = project.task("assemble${variant.name.capitalize()}Fir").doLast {
    println("开始上传Fir")
    def (String appPackage, String apiTokenFir, String apkPath, String fileName, String appName, String appVersion, String appBuild, String apkIconPath) = getParams(project, variant)
    OkHttpUtil okHttpUtil = new OkHttpUtil()
    BundleApp bundleApp = okHttpUtil.getCert(appPackage,apiTokenFir)
    println("获取凭证信息成功")
    BundleApp.CertBean certBean = bundleApp.getCert()

    // 上传apk
    println("上传apk中...")
    String key = certBean.getBinary().getKey()
    String token = certBean.getBinary().getToken()
    String upload_url = certBean.getBinary().getUpload_url()

    String jsonApk = okHttpUtil.uploadApk(apkPath,key,token,appName,appVersion,appBuild,fileName,upload_url)
    println("上传apk文件返回结果:$jsonApk")

    // 上传icon
    println("上传Icon中...")
    String keyIcon = certBean.getIcon().getKey()
    String tokenIcon = certBean.getIcon().getToken()
    String upload_urlIcon = certBean.getIcon().getUpload_url()

    String jsonIcon = okHttpUtil.uploadIcon(apkIconPath,keyIcon,tokenIcon,upload_urlIcon)
    println("上传Icon返回结果:$jsonIcon")

    ApkInfo apkInfo = okHttpUtil.getApkUrl(appPackage,apiTokenFir)
    println("下载链接:${apkInfo.installUrl}")

    def (String content, String title, String webHook, boolean isAtAll,List<String> atMobiles) = getDingTalkParams()
    String dingTalkMsg = "点击跳转gilos下载链接(版本号:$appBuild 版本名称:$appVersion)"
    if (content.length() > 0){
    dingTalkMsg = "${dingTalkMsg},此次更新:$content"
    }

    /**
    * 发送钉钉消息
    */
    okHttpUtil.sendDingTalkLink(dingTalkMsg,title,apkInfo.installUrl,webHook)
    okHttpUtil.sendDingTalkMsg(content,webHook,isAtAll,atMobiles)
    }

    // 在assembleDebug执行后执行
    uploadFir.dependsOn project.tasks["assemble${variant.name.capitalize()}"]
    }
    }
    }

    // 获取钉钉消息配置相关的参数并返回
    private List getDingTalkParams() {
    String webHook = extension.getDingTalkExtension().getWebHook()
    String title = extension.getDingTalkExtension().getTitle()
    String content = extension.getDingTalkExtension().getContent()
    String isAtAll = extension.getDingTalkExtension().getIsAtAll()
    List<String> atMobiles = extension.getDingTalkExtension().getAtMobiles()
    [content, title, webHook, isAtAll, atMobiles]
    }

    // 获取相关gradle配置文件和fir配置相关的参数并返回
    private List getParams(Project project, variant) {
    String appName = extension.getFirExtension().getAppName()
    String appPackage = project.android.defaultConfig.applicationId
    String appVersion = project.android.defaultConfig.versionName
    String appBuild = project.android.defaultConfig.versionCode
    String apkPath = variant.outputs.first().outputFile
    String fileName = apkPath.substring(apkPath.lastIndexOf("\\") + 1, apkPath.length())
    String apkIconPath = project.android.applicationVariants.first().outputs.first().outputFile.parent.split("build")[0] + extension.getFirExtension().getIconPath()
    String apiTokenFir = extension.getFirExtension().getToken()
    // 获取上传凭证
    // println("appName:$appName")
    // println("appPackage:$appPackage")
    // println("appVersion:${appVersion}")
    // println("appBuild:${appBuild}")
    // println("apiTokenFir:${apiTokenFir}")
    // println("apkIconPath:${apkIconPath}")
    println("文件路径:$apkPath")
    println("文件名称:$fileName")
    [appPackage, apiTokenFir, apkPath, fileName, appName, appVersion, appBuild, apkIconPath]
    }
    }

    声明插件

    在lib的配置文件xxx.xxx.xxx.properties(xxx.xxx.xxx是插件定义类所在的包名)中声明插件
    1
    com.demo.plugin.PluginImpl

    获取所有打包任务下的插件执行命令

    variant.name.capitalize()这个参数是打包任务名称首字母大写,如不确定当前的打包任务名称,可以增加app的gradle代码,打印所有的打包任务名称来获取插件的执行命令,同步一下即可输出
    1
    2
    3
    project.android.applicationVariants.all { variant ->
    println("assemble${variant.name.capitalize()}Fir")
    }

    插件上传

    上传到maven仓库

    上传本地仓库实际上就是生成maven工程的本地文件夹再引用,方便调试
    增加插件lib的gradle文件代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    uploadArchives {
    repositories {
    mavenDeployer {
    repository(url: uri('../repo')) // 上传到本地仓库调试
    // 上传到远程仓库
    // repository(url: "xxx.xxx.xxx:xxxx/repo私有仓库maven地址") {
    // authentication(userName: "用户名", password: "密码")
    // }
    pom.groupId = 'com.demo.plugin'//插件lib包名
    pom.artifactId = 'firPlugin'
    pom.version = '0.4' //插件版本号
    }
    }
    }
    执行gradle uploadArchives命令上传

    上传到远程仓库(私有maven/公有maven)

使用插件

  • 修改根目录的gradle文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // Top-level build file where you can add configuration options common to all sub-projects/modules.
    buildscript {
    ext.kotlin_version = "1.3.72"
    repositories {
    // maven { url './repo' } //本地Maven仓库地址
    // maven { url 'xxx.xxx.xxx:xxxx/repository/release'} // 私有仓库引用
    maven { url 'https://jitpack.io' } //Jitpack仓库引用

    google()
    jcenter()
    }
    dependencies {
    classpath "com.android.tools.build:gradle:4.0.0"
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    classpath 'com.github.jessieeeee:upload-apk-fir-plugin:0.7' //Jitpack插件引用
    // classpath 'com.demo.plugin:firPlugin:0.4' // 本地/私有仓库插件引用
    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
    }
    }

    allprojects {
    repositories {
    google()
    jcenter()
    }
    }

    task clean(type: Delete) {
    delete rootProject.buildDir
    }
  • 修改app的gradle文件,引入插件并进行相关配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    apply plugin: 'com.demo.plugin'
    uploadApk {
    fir {
    appName = "你的app名称"
    iconPath = "src/main/res/mipmap-xxxhdpi/ic_launcher.png"
    token = "fir平台的token"
    }
    dingTalk{
    webHook = "钉钉机器人的webhook"
    title = "Android:xxx打包完成"
    content = "带关键字的消息内容" //这个关键字跟自定义钉钉机器人的安全设置有关
    isAtAll = false // 是否at所有人
    atMobiles = ["手机号1","手机号2"] //at某些人
    }

    }
    github完整项目传送门,欢迎star,issue~