Android gradle打包并自动上传

作者: 战五渣_lei | 来源:发表于2017-09-20 08:29 被阅读502次

    简介

    这篇文章主要介绍Android用gradle打包,并且调用python脚本将打包好的apk上传到fir.im供相关人员下载,对于学习gradle 打包和python 几个常用网络库有一定帮助
    关键字 :Android Gradle Python fir.im

    1 开发前准备

    开发前需要下载 AndroidStudio ,gradle(3.3版本),python3.6,Curl,pycurl
    准备fir.im账号一个,有账号对应的apitoken,具体fir.im的细节可查看文档fir.im
    开发环境为macOS,windows推荐把pycurl部分全部替换为requests,即上传apk部分代码换为上传图标的那种方式,详细见代码

    2 Gradle 脚本编写

    我们脚本需要做的事情就是在APK打包完成以后,把apk的路径传递给python脚本即可,如何保证打包好了,就用gradle的dependsOn方法,例如打debug包,我们就可以写一个task ,这个task dependsOn ‘assembleDebug’,这样这个task调用时,会先执行assembleDebug,然后在执行里面的代码,即符合我们的需要
    具体代码

    //这个task 需要放在 app/build.gradle文件中的android 代码块里
    task assemblewithlog {
            dependsOn 'assembleDebug'
            doLast {
                //这个需要配置一下,app的图标
                def appicon = "app/src/main/res/drawable/icon.png"
                //这个需要配置一下,app的输出到fir.im的名称
                def outId = "yitiji_Debug"
    //取 apk的版本名和apk的输出文件目录
                def verName = project.android.defaultConfig.versionName;
    
                def apkpath = applicationVariants.first().outputs.first().outputFile
       //调用python脚本  这个脚本需要放在工程目录下
       def process = "python3 uploadfirim.py ${outId} ${verName} ${appicon} ${apkpath}".execute()
                // Wait till the process completes before continuing
                println("上传apk中")
    //将python代码里面打印的内容在gradle窗口中打印出来
                ByteArrayOutputStream result = new ByteArrayOutputStream();
                def inputStream = process.getInputStream()
                byte[] buffer = new byte[1024];
                int length;
                while ((length = inputStream.read(buffer)) != -1) {
                    result.write(buffer, 0, length);
                }
                println(result.toString("UTF-8"));
    //阻塞gradle代码直到python代码执行结束
                int exitValue = process.waitFor()
                println "上传结束 with value $exitValue"
            }
        }
    

    3 python脚本的编写

    这个python脚本有点学习的内容,所以使用了 urllib ,requests,pycurl三个网络请求库,实际上只用一种就好了
    这里关键是需要了解fir.im上传的请求格式和响应,细节可以看文档
    fir.im,
    主要是先将apk的标识信息和用户的apitoken传给fir.im,它会响应文件上传的地址和图标上传的地址,然后将对应的文件和信息传入返回的地址即可,最后在通过apk的标识信息和用户的apitoken可以查询到此apk在fir.im对应的下载页面
    那么就安装这三步来讲好了

    3.1 查询上传地址

    官方文档 如下

    调用示例
    
    curl -X "POST" "http://api.fir.im/apps" \
         -H "Content-Type: application/json" \
         -d "{\"type\":\"android\", \"bundle_id\":\"xx.x\", \"api_token\":\"aa\"}"
    
    响应示例
    
    # status: 201
    
    {
        "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"
            }
        }
    }
    
    

    所以按照文档写python3 代码如下

    #encoding = utf-8
    import traceback
    from urllib import request
    from urllib import parse
    import requests
    import pycurl
    import json
    from io import BytesIO
    
    import time
    
    import sys
    
    
    def uploadtofirim():
        minlen = 5
    #检查参数传递,定义好按照 name version 图标路径 apk路径 apitoken 的顺序传递参数
        syslen = len(sys.argv)
        if syslen < minlen:
            print("传递参数有误")
            return
        if syslen > 5:
            apitoken = sys.argv[5]
        else:
    #这是一个无效的token,只是为了展示用,需要替换为你自己的fir.imtoken
            apitoken = "7a15a28c75005akkkklllle051c71"
        appname = sys.argv[1]
        appversion = sys.argv[2]
        iconpath = sys.argv[3]
        apkpath = sys.argv[4]
    #利用urllib 请求并获取响应,数据格式见fir.im文档
        data = parse.urlencode({'type': 'android', 'bundle_id': appname, 'api_token': apitoken})
        datas = data.encode('utf-8')
        req = request.Request(url='http://api.fir.im/apps', data=datas, method='POST')
        icondict = {}
        binarydict = {}
        try:
            with request.urlopen(req) as f:
                strdec = f.read().decode('utf-8')
                resjson = json.loads(strdec)
    #将请求的结果存起来后面用
                icondict = (resjson["cert"]["icon"])
                binarydict = (resjson["cert"]["binary"])
        except:
            print("读取地址失败")
            pass
    

    3.2 上传图标和apk

    之前获取了路径,接下来将文件传上去,注意 python3对于https的请求有坑,如果代码报ssl错误,执行python3 按照目录下的Install Certificates.command文件 ,路径参考 /Applications/Python 3.6/Certificates.command
    官方文档如下,细节查阅官网

    调用示例
    
    curl   -F "key=xxxxxx"              \
          -F "token=xxxxx"             \
          -F "file=@aa.apk"            \
          -F "x:name=aaaa"             \
          -F "x:version=a.b.c"         \
          -F "x:build=1"               \
          -F "x:release_type=Adhoc"   \  #type=ios 使用
          -F "x:changelog=first"       \
          https://up.qbox.me
    
    响应示例
    
    # status: 201
    
    { "is_completed": true }
    
    

    所以对应的py脚本如下

    #接上面的py代码,复制时注意下格式和缩进
       try:
            local_filename = iconpath
            c = pycurl.Curl()
            print("上传图片")
            files = {'file':  open(local_filename, 'rb')}
            paramdata = {'key': icondict["key"],"token":icondict["token"]}
    #用 requests库上传图标文件并读取响应,verify=False是因为上传地址是https,不这样写会报错
            res = requests.post(icondict["upload_url"], files=files, data=paramdata,verify=False)
            print(res.text)
            local_apkfilename = apkpath
            timenow = str('time :' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
    #用 pycurl库上传apk文件并读取响应
            c.setopt(c.URL, binarydict["upload_url"])
            c.setopt(c.HTTPPOST, [
                ("file", (c.FORM_FILE, local_apkfilename)),
                ("key", binarydict["key"]),
                ("token", binarydict["token"]),
                ("x:name", appname),
                ("x:version", appversion),
                ("x:build", '1'),
                ("x:changelog", timenow)
            ])
            print("上传apk")
            c.perform()
            c.close()
            print("上传成功")
        except Exception as e:
            print(e)
            print("上传文件失败,请检查")
            return
    

    3.3获得apk文件的下载页面

    apk上传好了以后会生成下载分享页面,可以直接通过get请求拿到
    官方文档

    请求示例
    
     curl http://api.fir.im/apps/latest/xxx?api_token=xxx #使用 `id` 请求
     curl http://api.fir.im/apps/latest/im.fir.xxx?api_token=xxx&type=android #根据`bundle_id` 获取更新
    
    响应数据
    
    # status: 200
    
    {
      "name": "fir.im",
      "version": "1.0",
      "changelog": "更新日志",
      "versionShort": "1.0.5",
      "build": "6",
      "installUrl": "http://download.fir.im/v2/app/install/xxxxxxxxxxxxxxxxxxxx?download_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "install_url": "http://download.fir.im/v2/app/install/xxxxxxxxxxxxxxxx?download_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxx",   # 新增字段
      "update_url": "http://fir.im/fir",  # 新增字段
      "binary": {
        "fsize": 6446245
      }
    }
    
    

    我们用的就是bundle_id,这个bundleid就是我们gradle传进去的名字,返回的update_url 就是分享apk的页面

    所以py代码如下

    #接上面的py代码,复制时注意下格式和缩进
     queryurl='http://api.fir.im/apps/latest/%s?api_token=%s&type=android'%(appname,apitoken)
        print(queryurl)
        req = request.Request(url=queryurl,method="GET")
        try:
            with request.urlopen(req) as f:
                strdec = f.read().decode('utf-8')
                resjson = json.loads(strdec)
                print("apk下载地址 " + resjson["update_url"])
        except Exception as e:
            print(e)
            traceback.print_exc()
            print("读取地址失败")
            pass
    

    完整的py代码如下

    #encoding = utf-8
    import traceback
    from urllib import request
    from urllib import parse
    import requests
    import pycurl
    import json
    from io import BytesIO
    
    import time
    
    import sys
    
    
    def uploadtofirim():
        minlen = 5
        syslen = len(sys.argv)
        if syslen < minlen:
            print("传递参数有误")
            return
        if syslen > 5:
            apitoken = sys.argv[5]
        else:
            apitoken = "7akkkkkkkkkkkk1c71"
        appname = sys.argv[1]
        appversion = sys.argv[2]
        iconpath = sys.argv[3]
        apkpath = sys.argv[4]
        data = parse.urlencode({'type': 'android', 'bundle_id': appname, 'api_token': apitoken})
        datas = data.encode('utf-8')
        req = request.Request(url='http://api.fir.im/apps', data=datas, method='POST')
        icondict = {}
        binarydict = {}
        try:
            with request.urlopen(req) as f:
                strdec = f.read().decode('utf-8')
                resjson = json.loads(strdec)
                icondict = (resjson["cert"]["icon"])
                binarydict = (resjson["cert"]["binary"])
        except:
            print("读取地址失败")
            pass
        try:
            local_filename = iconpath
            c = pycurl.Curl()
            print("上传图片")
            files = {'file':  open(local_filename, 'rb')}
            paramdata = {'key': icondict["key"],"token":icondict["token"]}
            res = requests.post(icondict["upload_url"], files=files, data=paramdata,verify=False)
            print(res.text)
            local_apkfilename = apkpath
            timenow = str('time :' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
            c.setopt(c.URL, binarydict["upload_url"])
            c.setopt(c.HTTPPOST, [
                ("file", (c.FORM_FILE, local_apkfilename)),
                ("key", binarydict["key"]),
                ("token", binarydict["token"]),
                ("x:name", appname),
                ("x:version", appversion),
                ("x:build", '1'),
                ("x:changelog", timenow)
            ])
            print("上传apk")
            c.perform()
            c.close()
            print("上传成功")
        except Exception as e:
            print(e)
            print("上传文件失败,请检查")
            return
        queryurl='http://api.fir.im/apps/latest/%s?api_token=%s&type=android'%(appname,apitoken)
        print(queryurl)
        req = request.Request(url=queryurl,method="GET")
        try:
            with request.urlopen(req) as f:
                strdec = f.read().decode('utf-8')
                resjson = json.loads(strdec)
                print("apk下载地址 " + resjson["update_url"])
        except Exception as e:
            print(e)
            traceback.print_exc()
            print("读取地址失败")
            pass
    
    # appid vesion  icon apk apitoken
    if __name__ == '__main__':
        uploadtofirim()
    
    

    这样就好了
    在Android studio对应的工程里面执行代码 gradle assemblewithlog
    就开始打包debug并上传到fir.im了

    图片.png

    相关文章

      网友评论

        本文标题:Android gradle打包并自动上传

        本文链接:https://www.haomeiwen.com/subject/lwdnsxtx.html