本身動手開發一個Android持續集成工具-4

本系列代碼地址 githubjava

相關資料 gradle in action中英文對照版android

回到目錄git

咱們在上一邊已經將「publishReleaseRelease」和「assembleRelease」這兩個任務關聯起來了。並已經肯定了「publishReleaseRelease」執行前會先執行「assembleRelease」,接下來就是把「assembleRelease」執行完後生成的apk來上傳到蒲公英了。 咱們先來介紹下蒲公英。github

蒲公英官網api

蒲公英提供專業的手機應用內測服務,您只需將須要內測的應用上傳至蒲公英,生成二維碼,內測用戶經過在手機上掃描二維碼,便可將內測應用安裝至手機等設備中進行測試。app

當咱們把內測的apk上傳到蒲公英之後,會生成一個安裝頁面,以下:dom

這樣,測試人員就能夠在這個下載頁面下載對應的版本進行測試。並且這個頁面還會保存版本的歷史記錄,方便咱們進行版本對比。工具

咱們能夠在蒲公英的後臺進行手動上傳,但咱們要實現的是讓上傳過程自動化。post

蒲公英提供了一個api接口來提供apk文件上傳,網址。基本上是很簡單的,就不詳細說了,看文檔就能夠了。測試

接下來實現apk文件上傳功能。

咱們先寫一個工具類,來實現http的文件上傳功能,代碼以下:

class MultipartUtility {

    private final String boundary = UUID.randomUUID().toString()
    private static final String LINE_FEED = "\r\n"
    private HttpURLConnection httpConn
    private String charset
    private OutputStream outputStream
    private PrintWriter writer

    /** * This constructor initializes a new HTTP POST request with content type * is set to multipart/form-data * * @param requestURL * @param charset * @throws IOException */
    MultipartUtility(String requestURL, String charset)
            throws IOException {
        this.charset = charset

        URL url = new URL(requestURL)
// Log.e("URL", "URL : " + requestURL.toString());
        println "URL : " + requestURL.toString()

        httpConn = (HttpURLConnection) url.openConnection()

// httpConn = url.openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(8888)))


        httpConn.setRequestMethod("POST")
        httpConn.setUseCaches(false)
        httpConn.setDoOutput(true) // indicates POST method
        httpConn.setDoInput(true)
        httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary)
// httpConn.setRequestProperty("User-Agent", "CodeJava Agent")
// httpConn.setRequestProperty("Test", "Bonjour")
        outputStream = httpConn.getOutputStream()
        writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true)
    }

    /** * Adds a form field to the request * * @param name field name * @param value field value */
    void addFormField(String name, String value) {
        writer.append("--" + boundary).append(LINE_FEED)
        writer.append("Content-Disposition: form-data; name=\"" + name + "\"")
                .append(LINE_FEED)
        writer.append("Content-Type: text/plain; charset=" + charset).append(
                LINE_FEED)
        writer.append(LINE_FEED)
        writer.append(value).append(LINE_FEED)
        writer.flush()
    }

    /** * Adds a upload file section to the request * * @param fieldName name attribute in <input type="file" name="..." /> * @param uploadFile a File to be uploaded * @throws IOException */
    void addFilePart(String fieldName, File uploadFile) throws IOException {
        String fileName = uploadFile.getName()
        writer.append("--" + boundary).append(LINE_FEED)
        writer.append(
                "Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"")
                .append(LINE_FEED)
        writer.append("Content-Type: " + "application/vnd.android.package-archive")
                .append(LINE_FEED)
        writer.append("Content-Transfer-Encoding: binary")
                .append(LINE_FEED)
        writer.append(LINE_FEED)
        writer.flush()

        FileInputStream inputStream = new FileInputStream(uploadFile)

        byte[] buffer = new byte[1024 * 6]
        int bytesRead = -1
        int countBytes = 0
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead)
            outputStream.flush()
            countBytes += bytesRead
        }

        println "countBytes:${countBytes}"


        inputStream.close()
// writer.append(LINE_FEED)
        writer.flush()
    }

    /** * Adds a header field to the request. * * @param name - name of the header field * @param value - value of the header field */
    void addHeaderField(String name, String value) {
        writer.append(name + ": " + value).append(LINE_FEED)
        writer.flush()
    }

    /** * Completes the request and receives response from the server. * * @return a list of Strings as response in case the server returned * status OK, otherwise an exception is thrown. * @throws IOException */
    String finish() throws IOException {
        StringBuffer response = new StringBuffer()

        writer.append(LINE_FEED).flush()
        writer.append("--" + boundary + "--").append(LINE_FEED)
        writer.close()

        // checks server's status code first
        int status = httpConn.getResponseCode()
        if (status == HttpURLConnection.HTTP_OK) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    httpConn.getInputStream()))
            String line = null while ((line = reader.readLine()) != null) {
                response.append(line)
            }
            reader.close()
            httpConn.disconnect()
        } else {
            throw new IOException("Server returned non-OK status: " + status)
        }

        return response.toString()
    }
}

複製代碼

接下來咱們建立一個任務「uploadPgyerByApi」,代碼以下:

task uploadPgyerByApi() {
    doLast {
        //apk打包完成後存在的位置
        def file_path = "./build/outputs/apk/release/app-release-unsigned.apk"
        //apk上傳api url
        def api_url = "http://www.pgyer.com/apiv2/app/upload"
        //用戶api_key 可從蒲公英後臺獲取
        def api_key = "1540c89d7f12ade530a14ac4ad******"
        //用戶user_key 可從蒲公英後臺獲取
        def user_key = "15391e4e9f1d62962b97ff630d******"
        MultipartUtility utility = new MultipartUtility(api_url, "UTF-8")

        utility.addFormField("_api_key", api_key)
        utility.addFormField("userKey", user_key)
        utility.addFormField("buildInstallType", "2")
        utility.addFormField("buildPassword", "123456")
        utility.addFormField("buildUpdateDescription", "serverName")
        utility.addFilePart("file", file(file_path))

        def result = utility.finish()
        println result
    }
}

複製代碼

「uploadPgyerByApi」這個task在doLast這個action中實現了apk文件的上傳。「file_path」這參數指定了「assembleRelease」這個任務執行完成後生成的apk所在的位置,即咱們要上傳的apk的文件路徑。

這樣咱們apk文件上傳的task便完成了。接下來就須要把「uploadPgyerByApi」和「publishReleaseRelease」這兩個任務串在一塊兒。咱們在「publishReleaseRelease」這個task的最後樣加入以下代碼:

finalizedBy(uploadPgyerByApi)
複製代碼

即爲:

task publishReleaseRelease(dependsOn: "assembleRelease") {
    group "publish"

    doFirst {
        println "publishReleaseRelease doFirst called......"
    }
    doLast {
        println "publishReleaseRelease doLast called......"
    }

    finalizedBy(uploadPgyerByApi)
}

複製代碼

這樣咱們就實現了這樣一個流程: 1.打包應用 2.上傳apk文件到蒲公英

咱們執行「publishReleaseRelease」這個task,獲得以下結果:

咱們看到,「assembleRelease」,「publishReleaseRelease」,「uploadPgyerByApi」這三個任務依次執行了,咱們到蒲公英後臺看一下,發現apk已經上傳完成:

fir上傳的實現和蒲公英的基本一致,感興趣的同窗能夠本身實現如下,就當練習了。

相關文章
相關標籤/搜索