• 玩转app迭代开发


    玩转app迭代开发

    现在互联网开发都处于快速迭代开发的状态。这里我们来讨论一下迭代开发中的生命周期,代码版本,兼容性设计等话题。

    生命周期

    通常,一个产品都具有两个环境:

    1. 测试环境:用于开发,测试搭建的环境
    2. 线上环境:正式面向用户的环境

    而每个版本的生命周期通常会经历如下几个阶段(忽略需求和设计等辅助阶段):

    1. 开发:处于编码状态,需要不定时的发布到测试环境中进行测试
    2. 测试:本版本功能开发完成,此时将代码完整的发布到测试环境中,进行内部测试
    3. 预发布:此时APP的后台系统已经部署到线上环境中,但是还需要一部分用户(内部人员,被
      邀请的体验用户等)
      进行线上测试最新的APP,如果出现问题,能及时的回滚
    4. 正式发布:此时APP已经处于完全推广状态,通知旧版本的用户进行升级到最新的APP版本可用
      当然旧版本还是能保持一段时间的可用状态,不能马上不可用。

    所以针对APP,可能会出现同一个时间,线上存在多个不同版本的APP。所以我们需要注意:

    1. 测试的时候:需要测试那些比较活跃的老版本APP,而不是仅仅测试最新发布的APP。
    2. API开发的时候:需要保持活跃版本APP的API兼容性。

    代码版本

    在开发过程中,会遇到代码版本的管理问题,比如说:一个线上的BUG,如何进行修改和发布。这需要一个合理的代码版本管理。

    版本号

    通常,我们会采用x.y.z的格式来定义版本号,而考虑到机器和人的区别,出现了两种方式的版本号定义:

    • versionName:也就是x.y.z,便于人类识别的版本号
    • versionCode:通过x.y.z计算出来的数值,便于计算机识别的版本号

    versionName的公式为:

    versionName = x.y.z , 且 x.y.z 都为数字

    • x -> 主版本号,无上限
    • y -> 小功能版本号,上限 99(可以根据实际情况调整)
    • z -> 修订版本号(修复BUG),上限 99(可以根据实际情况调整)

    versionCode计算公式为:

    versionCode = x * 10000 + y * 100 + z 的格式命名

    通常采用versionCode作为系统内部的版本对比数值

    代码版本结构

    无论是SVN还是GIT通常都有 /tags,/branches,/trunk三个主要目录,而具体的使用方法为:

    /tags 快照目录,每次从/branches中发布到线上的代码,都需要标记tag到/tags下,以便于发现BUG进行修复
    	/1.1.0 发布的1.1.0版本
    	/1.1.1 发布的1.1.1版本,修复了/1.1.0发布的某些BUG
    	/1.2.0 发布的1.2.0版本
    
    /branches 开发目录,当开发或者修复到一定程度后,需要合并代码到/trunk中,并且/branches中的不同代码版本只能通过/trunk交换代码。开发或者修复完成后,需要合并代码到/trunk中,并且标记tag到/tags。
    	/1.1.x 1.1.x 功能版本
    	/1.2.x 1.2.x 功能版本
    /trunk 合并目录,最新稳定的代码版本,主要用于branches之间的代码交换,注意代码开发不在/trunk上。
    

    注意:对于APP的发布,从/branches/1.2.x 打tag到/tags/的时候,需要上传apk和mapping文件到/tags/1.2.0/release中,方便之后定位bug。

    兼容性设计

    基本概念

    这里写一些通用的概念:

    • APP版本废弃: 活跃用户不在这个被废弃的APP版本,并且服务器限制该版本APP使用。

    服务器

    在保持兼容的过程中,服务器的兼容性还是比较好处理的,因为服务器的升级完全处于我们自己的控制中,
    不会出现服务器处于不同的版本状态。所以,只要处理好数据库,HTTP接口,推送协议,基本能保证
    迭代无副作用。

    数据库

    在业务的开发过程中,不可避免的就是添加新的字段和删除原来旧的字段,对于新的字段添加,基本上可
    以保证旧的业务正常的运行,而删除字段,则可能导致旧的业务不能正常的运行,所以需要慎重选择。
    当然我们也不是不能删除字段,一般的做法是先保留旧的字段,然后等待APP版本废弃
    ,之后就可
    以进行删除该字段了。

    HTTP接口

    URL 兼容性

    对于HTTP接口,在迭代过程中,会出现如下几种情况:

    • 添加API
    • 删除API
    • 修改API
      • 添加字段
      • 删除字段

    这几种情况,为了保证已经上线的APP能正常的运行,所以我们需要对API做兼容性工作。API格式为:

    http://host/module/service[...N]

    当对一个API进行更新的时候,并且无法做到兼容的时候(删除字段等),我们新建一个

    http://host/module/service2

    这种格式的API,并且使用注解标记原先API为废弃状态(禁止新开发版本使用该API,并且版本迭代
    到后面几个版本后,可以剔除该API)。这种API设计方式有如下的优点:

    1. API更新的表示非常的简洁,只用添加版本号到最后即可
    2. 废弃的API和新的API都在同一个代码文件中,维护比较方便

    然而,这种方式有一个缺点:对同一个API修改的次数过多,会导致形成过多的 API 版本(1,2,3,4,5)
    不是非常的好看,我们可以通过设置一个版本滑动窗口来解决。

    版本滑动窗口:当API版本迭代到10的时候,自动又从0开始,当然了0版本号的api需要早早的被废弃掉。

    当然,还有其他解决URL兼容性的方法,比如说:采用 http://host/v1/module/service 的方式,但是这个方式
    存在一个比较致命的地方:每次修改API,都需要FORK出新的代码版本,而往往我们还需要维护旧版本代码,导致
    维护难度(修改同一个API的时候,需要打开该API的多个代码版本进行维护)。

    API扩展性

    不仅仅是API的URL需要保持兼容性,针对返回的数据,我们也需要做到一定程度的扩展性

    例子一 : 朋友圈主题不同类型

    在最早的时候,朋友圈还是只能处于发发图片和文字的状态,后来加入了一些额外的元素,
    音乐,视频,图文等内容。然而在加入了新的主题类型后,旧的用户不一定会马上升级,
    所以要保持朋友圈的兼容性。有大致两个做法:

    1. 添加host/module/server2这种类型的API,使得它支持新扩展的音乐,视频,图文
    2. 返回数据结构中添加TYPE属性,APP仅仅处理能识别TYPE的数据,过滤掉不能识别TYPE的数据(音频,图文等)。

    方案1虽然说也能处理这些情况,但是总体来说没有方案2来的更加优雅。

    TOKEN 认证

    对于HTTP接口设计中身份验证,通常采用TOKEN的方式。因为TOKEN比较方便存储,以及显示的使用。

    返回JSON

    HTTP 返回的数据格式,通常采用JSON格式:{code:-1|0|1,msg:String,data:any}。

    • code: 状态码,负数为系统统一错误代码,0为成功,正数为业务错误代码。
    • msg:执行结果提示,String类型
    • data:需要返回的数据,可能为空,也可以为任何数据类型JSONObject,JSONArray,Int,Double,String.
    HTTPS

    为了通信的安全,我们还需要采用HTTPS连接方式,避免中间人攻击等常见的网络攻击手段。

    推送协议

    开发移动APP的时候,不可避免的就是使用长连接推送一些及时消息,比如说新闻,广告,强制下线通知等。而推送协议的格式基本上为:

    { module: 模块类型, function:操作类型, [data]:{msg:"hello world"}}。

    当APP端接收到推送的时候。采用如下的步骤处理推送:

    1. 判断业务模块module是否能处理
    2. 判断function是否能进行处理
    3. 读取data数据
    4. 根据 module,function 使用data作为参数,调用具体的处理函数。

    处理过程中,注意使用

    try{
    	...logic
    }catch(Exception e){
    
    }
    

    避免处理过程中,导致APP奔溃。

    当出现推送协议完全不兼容(删除字段,删除含义等)的情况的时候,我们需要针对推送协议做兼容处理,通常我们可以采用function[..N]的方式来兼容。在兼容期阶段,我们可以采用:

    1. 检测连接APP的版本,选择正确的推送格式function 还是 function2 格式
    2. 同时发送function 和 function2 协议,新版本APP删除function支持,仅仅处理function2。而旧版本APP仅仅只能处理function协议,忽略了function2的协议处理。
    3. 预先埋点,在几个版本之前就嵌入function2的逻辑,当function逻辑的版本APP都废弃后,就可以统一采用function2 协议推送。

    上述两种方法各有优缺点:方案1比较智能,但是开发量比较大。而方案二比较方便,但是比较损耗流量。而方案3需要时间来过度。

    移动端

    移动端升级,主要涉及到本地数据(数据库,磁盘文件),APP全局升级补丁,协议兼容性(HTTP,推送),
    以及APP版本检测,
    而JAVA代码和资源文件,当然就不需要进行处理了。

    数据库

    数据库升级还是比较方便的,这里我们采用APP版本代码(versionCode)作为SQLite的版本号
    SQLite已经为我们提供好了onUpgrade接口,在升级的时候,SQLite会给出旧的版本号,和最新的版本号,
    此时我们只要做:

    app sqlite upgrade

    就可以保证对数据库的正确升级了。

    App全局升级补丁

    除了数据库升级,对于磁盘文件,升级后的引导页面等需要处理的杂项,可以在APP全局升级补丁中进行
    处理。比如说,某个版本,可以删除某些磁盘文件,那么,就可以在全局补丁中编写代码。具体的做法为:

    1. 每次打开APP初始化application的时候onCreate(),读取记录在本地数据库的APP版本号和APK中的versionCode进行对比
    2. 如果不一致,那么,就有可能是升级后,重新打开APP的情况,那么,我们就可以进行升级补丁操作
    3. 升级完成后, 把最新的APK中的versionCode写入本地数据库,避免下一次打开APP的时候,重复升级

    升级代码大致如下:

    app upgrade

    可以发现,使用versionCode作为升级代码点,能比较清晰的区分出不同版本做了什么事情。而且对于从比较老的版本直接升级过来,也是OK的。

    APP版本检测

    APP版本检测和是升级是比较核心的功能模块。主要实现的功能有:

    1. 定时检测 APP 是否需要升级,并且进行提示。
    2. 判断用户是否可以继续使用该APP,即当前APP需要强制升级。

    所以,APP检测的时候,给定服务器最新的版本信息数据即可,格式基本是这个样子:

    app version

    含义:

    • versionName:最新版本名称 x.y.z
    • versionCode: 最新版本代码
    • deadVersionCode:如果APP#versionCode <= Server#deadVersionCode,需要进行强制升级,不能再使用其他功能。

    当检测到新的APP版本后,还需要在界面上进行ALERT,通知用户进行升级。强制升级的话,需要禁用所有
    的功能(避免APP奔溃)。然后进行ALERT用户。

    APP版本检测功能,应该算是APP快速迭代的保证。可以让服务端来控制APP是否可以继续使用和升级。

    总结

    当然了,还有一些其他方面的迭代开发话题,之后再补充上去。

    参考

  • 相关阅读:
    gdb调试
    go pipeline
    Go的Timer
    goconvey
    购物
    Go的可行测试
    可能会停止一段时间的更新
    一些blog
    linux全套 | Python开发平台_Ubuntu | 15
    Python基础 | 配置pip镜像源 | 01
  • 原文地址:https://www.cnblogs.com/darkgem/p/5428323.html
Copyright © 2020-2023  润新知