自定义Gradle插件实现打包上传到蒲公英

上一篇文章提到了关于如何写Gradle自定义插件Plugin,本篇文件介绍下如何写gradle插件实现打包上传到pgy。

要实现打包上传到蒲公英必须要弄清楚以下问题:

1、什么时候打包完成?监听到打包完成才能执行下一步的上传操作

Gradle的打包也是一组组的Task任务,每个任务都有一个dependsOn()方法,我们只要在Assemble打包任务执行完了以后,再执行就可以了,即让我们的上传任务dependsOn打包任务就可以了。

2、怎么上传?

蒲公英api文档:http://www.pgyer.com/doc/api#uploadApp

3、build.gradle文件内配置的参数怎么获取?

通过org.gradle.api.Project这类获取。Project是支持扩展的,我们可以自定义一个Extension的数据类型,作为Project的一个扩展。

在Project里面有个回调方法afterEvaluate(),它会在构建完成后执行。我们需在这个方法里,获取自定义的Extension就可以了,即执行:project.getExtensions().create(PLUGIN_EXTENSION_NAME, PGYExtension.class)


先看下工程目录:
在这里插入图片描述

具体实现:

1、包含配置参数的模型:

package com.shengqf.plugin;

import org.gradle.api.Project;

public class PGYExtension {

    public String uKey;
    public String apiKey;
    public String installType; //1:公开,2:密码安装,3:邀请安装。默认为1公开
    public String password; 安装密码
    public String updateDescription; //版本更新描述,请传空字符串,或不传


    public PGYExtension() {
    }

    public PGYExtension(String uKey, String apiKey, String installType, String password, String updateDescription) {
        this.uKey = uKey;
        this.apiKey = apiKey;
        this.installType = installType;
        this.password = password;
        this.updateDescription = updateDescription;

    }

    public static PGYExtension getConfig(Project project) {
        PGYExtension extension = project.getExtensions().findByType(PGYExtension.class);
        if (extension == null) {
            extension = new PGYExtension();
        }
        return extension;
    }

}

2、上传apk的工具类:

package com.shengqf.plugin;

import com.android.build.gradle.api.BaseVariant;
import com.android.build.gradle.api.BaseVariantOutput;

import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.tasks.TaskAction;

import java.io.File;
import java.io.IOException;

import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;


public class PGYUploadTask extends DefaultTask {

    private BaseVariant mVariant;
    private Project mProject;

    public void init(BaseVariant variant, Project project) {
        this.mVariant = variant;
        this.mProject = project;
        setDescription("upload apk to pgy");
        setGroup(UploadPlugin.PLUGIN_EXTENSION_NAME);
    }

    static class PGYRequest {
        public String uKey;
        public String apiKey;
        public String installType; //1:公开,2:密码安装,3:邀请安装。默认为1公开
        public String password; 安装密码
        public String updateDescription; //版本更新描述,请传空字符串,或不传
    }

    @TaskAction
    public void uploadToPGY() {
        for (BaseVariantOutput output : mVariant.getOutputs()) {
            File file = output.getOutputFile();
            if (file == null || !file.exists()) {
                throw new GradleException("apk file is not exist!");
            }
            PGYExtension extension = PGYExtension.getConfig(mProject);
            PGYRequest request = new PGYRequest();
            request.apiKey = extension.apiKey;
            request.uKey = extension.uKey;
            request.installType = extension.installType;
            request.password = extension.password;
            request.updateDescription = extension.updateDescription;
            upload(request.uKey, request.apiKey, request.installType,
                    request.password, request.updateDescription, file);
        }

    }

    /**
     * 蒲公英上传apk的API文档:http://www.pgyer.com/doc/api#uploadApp
     */
    private void upload(String ukey, String apiKey, String installType,
                        String password, String updateDescription, File apkFile) {
        //builder
        MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        //add part
        bodyBuilder.addFormDataPart("uKey", ukey);
        bodyBuilder.addFormDataPart("_api_key", apiKey);
        bodyBuilder.addFormDataPart("installType", installType);
        bodyBuilder.addFormDataPart("password", password);
        bodyBuilder.addFormDataPart("updateDescription", updateDescription);

        //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.Builder clientBuilder = new OkHttpClient.Builder();
        addInterceptor(clientBuilder);
        try {
            Response response = clientBuilder.build().newCall(request).execute();
            String result = response.body().string();
            System.out.println("upload result: " + result);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception ignored) {

        }
    }

    //打印上传进度
    private void addInterceptor(OkHttpClient.Builder clientBuilder) {
		...
    }
}

3、自定义插件:

package com.shengqf.plugin;

import com.android.build.gradle.AppExtension;
import com.android.build.gradle.api.ApplicationVariant;

import org.gradle.api.Action;
import org.gradle.api.DomainObjectSet;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.tasks.TaskContainer;

public class UploadPlugin implements Plugin<Project> {

    public static final String PLUGIN_EXTENSION_NAME = "upload";

    @Override
    public void apply(Project project) {
        PGYExtension extension = project.getExtensions().create(PLUGIN_EXTENSION_NAME, PGYExtension.class);
        project.afterEvaluate(new Action<Project>() {
            @Override
            public void execute(Project project) {
                DomainObjectSet<ApplicationVariant> appVariants = getAppVariants(project);
                if (appVariants == null) {
                    return;
                }
                for (ApplicationVariant variant : appVariants) {
                    String buildTypeName = variant.getBuildType().getName();
                    System.out.println("buildTypeName === " + buildTypeName);
                    if (buildTypeName.equalsIgnoreCase("release")
                            || buildTypeName.equalsIgnoreCase("debug")) {
                        String variantName = variant.getName().substring(0, 1).toUpperCase() + variant.getName().substring(1);
                        String taskName = "upload" + variantName;
                        TaskContainer taskContainer = project.getTasks();
                        PGYUploadTask pgyUploadTask = taskContainer.create(taskName, PGYUploadTask.class);
                        pgyUploadTask.init(variant, project);

                        variant.getAssembleProvider().get().dependsOn(project.getTasks().findByName("clean"));
                        pgyUploadTask.dependsOn(variant.getAssembleProvider().get());
                    }
                }
            }
        });
    }

    private DomainObjectSet<ApplicationVariant> getAppVariants(Project project) {
        AppExtension appExtension = (AppExtension) project.getExtensions().findByName("android");
        if (appExtension == null) {
            return null;
        }
        return appExtension.getApplicationVariants();
    }
}

4、app引入自定义插件,并配置蒲公英的参数:
在这里插入图片描述
注意,配置参数的名称upload必须和通过project获取Extension的create方法的第一个参数名一致,即:
在这里插入图片描述
这样,就可以打包上传到蒲公英自动化了,右侧grale面板 ,可以看到app的Tasks下多了upload的任务(面板里的upload任务名称由创建Extension的create方法的第一个参数名决定):
在这里插入图片描述
点击uploadDebug即可打debug包自动上传到蒲公英
点击uploadRelease即可打release包自动上传到蒲公英


补充:

1、因为用了okhttp,所以buildSrc这个module的build.gradle文件需要引入okhttp的依赖:

apply plugin: 'java-library'
apply plugin: 'groovy'
apply plugin: 'maven'

repositories {
    google()
    mavenCentral()
}

dependencies {
    implementation 'com.android.tools.build:gradle:3.5.2'
    implementation("com.squareup.okhttp3:okhttp:3.8.1")
    implementation gradleApi()  // gradle api
    implementation localGroovy()  // groovy api
}

2、上文中上传任务打印上传进度方法addInterceptor:

    private long tempBytes;

    //打印上传进度
    private void addInterceptor(OkHttpClient.Builder clientBuilder) {
        clientBuilder.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request original = chain.request();

                Request request = original.newBuilder()
                        .method(original.method(), new ProgressRequestBody(original.body(), new ProgressRequestListener() {
                            @Override
                            public void onRequestProgress(long bytesWritten, long contentLength, boolean done) {
                                if (!done) {
                                    if (bytesWritten - tempBytes > 1024 * 512) {
                                        tempBytes = bytesWritten;
                                        System.out.println("-------upload progress-------" + bytesWritten * 100 / contentLength + "%");
                                    }
                                } else {
                                    System.out.println("-------upload progress-------100%");
                                }
                            }
                        }))
                        .build();
                return chain.proceed(request);
            }
        });
    }

涉及到的类:

package com.shengqf.plugin;

import java.io.IOException;

import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.ForwardingSink;
import okio.Okio;
import okio.Sink;

public class ProgressRequestBody extends RequestBody {
    private  RequestBody requestBody;
    private  ProgressRequestListener progressListener;
    private BufferedSink bufferedSink;

    public ProgressRequestBody(RequestBody requestBody, ProgressRequestListener progressListener) {
        this.requestBody = requestBody;
        this.progressListener = progressListener;
    }

    @Override
    public MediaType contentType() {
        return requestBody.contentType();
    }

    @Override
    public long contentLength() throws IOException {
        return requestBody.contentLength();
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        if (bufferedSink == null) {
            bufferedSink = Okio.buffer(sink(sink));

        }
        requestBody.writeTo(bufferedSink);
        bufferedSink.flush();


    }

    private Sink sink(Sink sink) {
        return new ForwardingSink(sink) {
            long bytesWritten = 0L;
            long contentLength = 0L;

            @Override
            public void write(Buffer source, long byteCount) throws IOException {
                super.write(source, byteCount);
                if (contentLength == 0) {
                    contentLength = contentLength();
                }
                bytesWritten += byteCount;
                if(progressListener != null){
                    progressListener.onRequestProgress(bytesWritten, contentLength, bytesWritten == contentLength);
                }
            }
        };
    }
}

涉及到的接口:

package com.shengqf.plugin;

public interface ProgressRequestListener {
    void onRequestProgress(long bytesWritten, long contentLength, boolean done);
}

在别的项目中使用,只需将buildSrc模块拷贝到项目中(注意,settings.gradle不用加buildSrc的配置),然后在项目的app模块的build.gradle文件引入自定义插件、配置蒲公英参数即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值