U8SDK——渠道SDK的动态升级(实现)
上次我们已经在每个SDK的配置目录下的config.xml中,增加了一个version节点,来配置当前SDK使用的版本。其中versionCode,就是用来做渠道SDK动态更新使用的。本节,我们就来基于这个versionCode实现渠道SDK的更新机制。根据上一篇我们说的思路,为了做到动态更新机制,我们需要做两部分工作。第一:打包工具可以生成一份当前最新的渠道SDK版本更新控制文件,同时将所有需要更新的渠道SDK生成一个zip文件。第二:打包工具需要有更新渠道SDK的机制,我们需要去访问一个服务器地址,然后获取到服务器上的版本更新控制文件,然后比对本地的版本控制文件和远程上的版本控制文件,找到当前需要更新的渠道SDK信息,然后下载服务器上需要更新的渠道SDK的zip文件,下载完成之后,解压,并覆盖到本地的渠道SDK配置目录下。对于第一步,我们增加一个脚本(generate_sdk_update.py:)来完成渠道SDK更新文件的生成,直接上代码,看代码以及注释:
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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
#!/usr/bin/python # -*- coding: utf-8 -*- # Author xiaohei # Date 2015-06-18 try: input = raw_input except NameError: pass import os,os.path import shutil,errno import sys import time from xml.etree import ElementTree as ET from xml.etree.ElementTree import SubElement from xml.etree.ElementTree import Element from xml.etree.ElementTree import ElementTree def generate_updates(): """ 扫描渠道SDK配置目录下的所有渠道SDK下的config.xml,根据里面配置的versionCode来生成 需要更新的渠道SDK文件。 每个渠道SDK生成一个更新文件,格式为zip 最后生成一个version.txt,作为版本控制文件 生成的目录为updates 每次只生成有更新的渠道SDK,也就是将扫描的结果和updates目录下已经存在的version.txt 进行比对,生成新增的渠道SDK,以及versionCode有增加的渠道SDK """ print(u"generating sdk update files, please wait...") rootPath = "./config/sdk" if not os.path.exists(rootPath): print("The sdks folder is not exists."+os.path.abspath(rootPath)) return outputPath = "./updates" if not os.path.exists(outputPath): os.makedirs(outputPath) backup_version_file(outputPath) sdkList = get_old_versions(outputPath) for sdk in os.listdir(rootPath): if os.path.isfile(sdk): continue sdkConfig = rootPath + "/" + sdk + "/config.xml" if not os.path.exists(sdkConfig): print("There is no config.xml file in " + sdk) continue versionCode = parse_sdk_version(sdk, sdkConfig) if versionCode == None: continue if not is_need_update(outputPath, sdk, versionCode, sdkList): continue print("generate update of sdk %s..." % sdk) sdkPath = os.path.join(rootPath, sdk) zipName = sdk + "_%s" % time.strftime('%Y%m%d%H%M%S') zipFilePath = os.path.join(outputPath, zipName) shutil.make_archive(zipFilePath, "zip", sdkPath) if not os.path.exists(zipFilePath+".zip"): print("The zip file of sdk %s generate faild. %s" % (sdk, os.path.abspath(zipFilePath+".zip"))) continue sdkList.append([sdk, versionCode, zipName+".zip", os.path.getsize(zipFilePath+".zip")]) print("generate update of sdk %s success." % sdk) generate_version_file(outputPath, sdkList) def is_need_update(outputPath, sdk, newVersion, oldSDKList): """ 判断当前渠道SDK是否需要更新,根据versionCode判断 """ for s in oldSDKList: if sdk == s[0]: if newVersion > s[1]: oldFile = os.path.join(outputPath, s[2]) if os.path.exists(oldFile): os.remove(oldFile) oldSDKList.remove(s) print("remove old exists update file of sdk %s " % sdk) return True else: return False return True def generate_version_file(outputPath, sdkList): """ 生成version.txt """ if sdkList == None or len(sdkList) <= 0: print("generate version.txt faild. no sdk selected.") return versionFile = open(outputPath+"/version.txt", "w") for sdk in sdkList: versionFile.write("[%s,%s,%s,%s]\n" % (sdk[0], sdk[1], sdk[2], sdk[3])) versionFile.close() print("generate version.txt success.") def backup_version_file(outputPath): """ 备份上一次的version.txt """ oldVersionFile = os.path.join(outputPath, "version.txt") if not os.path.exists(oldVersionFile): return shutil.copy(oldVersionFile, os.path.join(outputPath, 'version_back.txt')) def get_old_versions(outputPath): """ 解析上一次生成的version.txt """ oldVersionFile = os.path.join(outputPath, "version.txt") if not os.path.exists(oldVersionFile): return [] sdkList = [] of = open(oldVersionFile, 'r') lines = of.readlines() of.close() if lines == None or len(lines) <= 0: return [] for line in lines: line = line.strip() line = line[1:] line = line[:-1] sdkList.append(line.split(',')) return sdkList def parse_sdk_version(sdk, configXml): """ 从渠道SDK配置目录下的config.xml中解析出versionCode """ try: tree = ET.parse(configXml) root = tree.getroot() except: print("Can not parse %s config.xml :path:%s" % sdk, configXml) return None versionNode = root.find('version') if versionNode == None: print("There is no version node in config.xml of sdk %s" % sdk) return None versionCodeNode = versionNode.find('versionCode') if versionCodeNode == None: print("The versionCode is not exists in [version] node of sdk %s" % sdk) return None return versionCodeNode.text if __name__ == "__main__": generate_updates() input("Press Enter to continue:") |
对于第二步,我们增加一个脚本(check_update.py:)来检查渠道SDK的更新,以及下载等操作,同样,直接上代码,看代码和注释:
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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
#!/usr/bin/python # -*- coding: utf-8 -*- # Author xiaohei # Date 2015-06-18 import os,os.path import shutil,errno import sys import time import config_utils import http_utils from xml.etree import ElementTree as ET from xml.etree.ElementTree import SubElement from xml.etree.ElementTree import Element from xml.etree.ElementTree import ElementTree try: input = raw_input except NameError: pass def check_sdk_update(): """ 检查版本更新,在config/local目录下,存放一个打包工具使用的配置文件local.properties 在该配置文件中,配置渠道SDK更新的服务器地址sdk_update_url. 每次更新将远程的version.txt存放在local目录下 用于下一次更新时,将远程的version.txt和本地的version.txt进行比对,从而筛选出需要 更新的渠道SDK 根据version.txt比对的结果,下载服务器上的需要更新的渠道SDK的zip文件 临时放在local目录下 然后解压到渠道SDK配置目录下,同时删除zip文件 """ print("checking update, please wait...") local_config = config_utils.getLocalConfig() if "sdk_update_url" not in local_config: print("the sdk_update_url is not exists in local.properties. check update failed.") return localUpdatePath = './local' if not os.path.exists(localUpdatePath): os.makedirs(localUpdatePath) sdkPath = './config/sdk' if not os.path.exists(sdkPath): os.makedirs(sdkPath) updateUrl = local_config['sdk_update_url'] updateVersionUrl = updateUrl + "version.txt" old_updates = get_old_versions(localUpdatePath) new_updates = get_new_versions(localUpdatePath, updateVersionUrl) olds = [] for sdk in new_updates: for old_sdk in old_updates: if sdk[0] == old_sdk[0] and sdk[1] == old_sdk[1]: olds.append(sdk) break new_updates = [sdk for sdk in new_updates if sdk not in olds] updateCount = len(new_updates) if updateCount <= 0: print("There is no sdk need update.") else: input("Total %s sdk to update, Press Enter to update:" % updateCount) for sdk in new_updates: print("Now to download %s ..." % sdk[0]) url = updateUrl + sdk[2] zipFile = os.path.join(localUpdatePath, sdk[2]) content = http_utils.get(url, None) f = open(zipFile, 'wb') f.write(content) f.close() print("%s update success, now to unzip..." % sdk[0]) currsdkPath = os.path.join(sdkPath, sdk[0]) if os.path.exists(currsdkPath): shutil.rmtree(currsdkPath) os.makedirs(currsdkPath) shutil.unpack_archive(zipFile, currsdkPath) os.remove(zipFile) def get_new_versions(localUpdatePath, updateVersionUrl): """ 获取服务器上的version.txt文件,同时将该version.txt存放到local/目录下 """ content = http_utils.get(updateVersionUrl, None) if content == None: return [] lines = content.decode('utf-8').split('\n') if lines == None or len(lines) <= 0: return [] #save the new version.txt versionFile = os.path.join(localUpdatePath, "version.txt") f = open(versionFile, "w") f.writelines(lines) f.close() sdkList = [] for line in lines: line = line.strip() if line == None or len(line) <= 0: continue line = line[1:] line = line[:-1] sdkList.append(line.split(',')) return sdkList def get_old_versions(localUpdatePath): """ 从本地local目录下解析version.txt 获取上次版本更新的记录信息 """ oldVersionFile = os.path.join(localUpdatePath, "version.txt") if not os.path.exists(oldVersionFile): return [] sdkList = [] of = open(oldVersionFile, 'r') lines = of.readlines() of.close() if lines == None or len(lines) <= 0: return [] for line in lines: line = line.strip() if line == None or len(line) <= 0: continue line = line[1:] line = line[:-1] sdkList.append(line.split(',')) return sdkList if __name__ == "__main__": check_sdk_update() input("Press Enter to continue:") |
这样,你在config/local/local.properites文件中配置一个更新地址,比如:sdk_update_url=http://192.168.3.32/sdk_update/
然后每次,SDK接入小伙伴接好一些SDK并测试通过之后,就可以使用generate_sdk_update.py生成更新的渠道SDK,生成的更新文件在updates目录下
将updates目录下所有的文件拷贝到刚刚配置的服务器上即可。
这样运营或者其他负责打包的小伙伴,在打包工具运行的时候,先调用check_sdk_update.py去检查当前是否有渠道SDK需要更新,如果有,自动更新即可。
这样,以后渠道SDK的接入和打包就可以很好的分开操作啦。当然,不采用这种方式,也可以采用之前@暖暖同学说的,远程打包处理。
如果,觉得u8sdk里的文章对你有用,可以看看我们提供的增值服务哦,感谢您对u8sdk的支持,我们会一直坚持技术开源,并分享一切u8sdk的实现原理。
本文出自 U8SDK技术博客,转载时请注明出处及相应链接。
本文永久链接: http://www.uustory.com/?p=1819