Android gradle打包并自动上传

简介

这篇文章主要介绍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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容