安卓系统如何进行update包升级流程?如何灵活配置安卓升级机制?

android系统升级分析(一)---update包升级流程分析 干货贴:如何设置灵活可配置的安卓升级机制?

本文目录:

最近在整理以前做过的工程知识,升级是必不可少的功能。接下来几篇文章都是关于这方面的,今天我们直接来看下android中具体的升级过程是如何实现的。

升级流程概述

升级的流程图:

升级流程分析

第一步:升级包获取

升级获取可以通过远程下载,也可直接拷贝到指定目录即可。

第二步:准备升级

然后调用RecoverySystem.installPackage将目录通过传参方式传递下去。这是应用层代码如下:

public void updateSystem(String updateSavePath) { File packageFile = new File(updateSavePath); try { //调用升级接口 RecoverySystem.installPackage(this, packageFile); } catch (IOException e) { LOG.e(TAG, "RecoverySystem ERROR!!!"); e.printStackTrace(); String errDesc =String.format("execute system update fail. reason-> %s", e.toString()); reportUpdateError(errDesc); }LOG.d(TAG, "RecoverySystem OK!!!");copyUpdateFileIsSuccess = false;}

在应用层下载升级包后,会调用RecoverySystem.installPackage(Context context, FilepackageFile)函数来发起安装过程,这个过程主要的原理,实际上只是往 /cache/recovery/command写入升级包存放路径,然后重启到recovery模式,仅此而已。

public static void installPackage(Context context, File packageFile) throws IOException { String filename = packageFile.getCanonicalPath(); Log.w(TAG, "!!! REBOOTING TO INSTALL "   filename   " !!!");//升级包路径final String filenameArg = "--update_package="   filename;final String localeArg = "--locale="   Locale.getDefault().toString();bootCommand(context, filenameArg, localeArg);}private static void bootCommand(Context context, String… args) throws IOException { RECOVERY_DIR.mkdirs(); // In case we need it COMMAND_FILE.delete(); // In case it's not writable LOG_FILE.delete(); FileWriter command = new FileWriter(COMMAND_FILE); try { for (String arg : args) { if (!TextUtils.isEmpty(arg)) { command.write(arg); command.write("\n"); } } } finally { command.close(); } // Having written the command file, go ahead and reboot PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); //重启到recovery模式 pm.reboot(PowerManager.REBOOT_RECOVERY); throw new IOException("Reboot failed (no permissions?)");}

PowerManager.REBOOT_RECOVERY的值为字符串"recovery",pm.reboot()方法最后会将recovery写入到misc分区的BCB中去。

BCB即Bootloader Control Block,启动控制信息块,位于misc分区,从代码上看,就是一个结构体。如下:

struct bootloader_message { char command[32]; char status[32]; char recovery[1024]; };

bootloader_message 结构体包含三个字段,具体含义如下:

command 字段中存储的是命令,它有以下几个可能值:

  • boot-recovery:系统将启动进入Recovery模式
  • update-radia 或者 update-hboot:系统将启动进入更新firmware的模式,这个更新过程由bootloader完成
  • NULL:空值,系统将启动进入Main System主系统,正常启动。

status 字段存储的是更新的结果。更新结束后,由Recovery或者Bootloader将更新结果写入到这个字段中。

recovery 字段存放的是recovry模块的启动参数,一般包括升级包路径。其存储结构如下:第一行存放字符串“recovery”,第二行存放路径信息“–update_package=/mnt/sdcard/update.zip”等。因此,参数之间是以“\n”分割的。

第三步:系统重启进入Recovery模式,进行升级操作

Android系统进行升级的时候,有两种途径:

一种是通过接口传递升级包路径自动升级(Android系统SD卡升级),升级完之后系统自动重启。另一种是手动进入recovery模式下,选择升级包进行升级,升级完成之后停留在recovery界面,需要手动选择重启。

前者多用于手机厂商的客户端在线升级,后者多用于开发和测试人员。但不管哪种,原理都是一样的,都要在recovery模式下进行升级。

下面介绍的是升级包保存在cache目录下,且升级包路径保存在/cache/recovery/command中的方式(升级包的存放路径,从BCB或者/cache/recovery/command里面解析得到的)。

重启进入升级主要流程:

  • 系统重启进入Recovery模式。读取BCB的command,读取到”boot-recovery”后,加载recovery.img,启动recovery。
  • 在install.cpp进行升级操作
  • try_update_binary执行升级脚本
  • 调用finish_recovery方法,清除BCB信息,重启

1、系统重启进入Recovery模式

系统重启时会判断/cache/recovery目录下是否有command文件,如果存在就进入recovery模式,否则就正常启动。

进入到Recovery模式下,将执行recovery.cpp的main函数,下面贴出关键代码片段:

… …while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) { switch (arg) { case 's': send_intent = optarg; break; case 'u': update_package = optarg; break; … … } … …

2、在install.cpp进行升级操作

在main函数中最终会调用到install_package方法。

… …if (update_package != NULL) { status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true); … …} … …

具体的升级过程都是在install.cpp中执行的,再来看看install_package方法。

int install_package(const char* path, int* wipe_cache, const char* install_file, bool needs_mount) { … … if (setup_install_mounts() != 0) { … … } else { result = really_install_package(path, wipe_cache, needs_mount); } … …}

install_package方法中创建了log文件(记录升级报错的信息)。然后调用really_install_package方法。

static int really_install_package(const char path, int wipe_cache, bool needs_mount) { … … // 装入签名文件 Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); … … // 验证签名 err = verify_file(map.addr, map.length, loadedKeys, numKeys); … … // 打开升级包 err = mzOpenZipArchive(map.addr, map.length, &zip); … … // 执行升级脚本文件,开始升级 int result = try_update_binary(path, &zip, wipe_cache); … …}

really_install_package方法中,首先验证签名,再读取升级包,然后进行调用try_update_binary方法升级

3、try_update_binary执行升级脚本

static int try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);... ...const char* binary = "/tmp/update_binary";unlink(binary);int fd = creat(binary, 0755); ... ...//将升级包里面的update_binary解压到/tmp/update_binarybool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);... ...const char** args = (const char**)malloc(sizeof(char*) * 5);args[0] = binary; //update_binary存放路径args[1] = EXPAND(RECOVERY_API_VERSION); // Recovery版本号char* temp = (char*)malloc(10);sprintf(temp, "%d", pipefd[1]);args[2] = temp;args[3] = (char*)path; //升级包存放路径args[4] = NULL;pid_t pid = fork();//fork一个子进程if (pid == 0) { close(pipefd[0]); //子进程调用update-binary执行升级操作 execv(binary, (char* const*)args); fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); _exit(-1);}//父进程负责接受子进程发送的命令去更新ui显示 ... ...int status;waitpid(pid, &status, 0);if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { //安装失败,返回INSTALL_ERROR return INSTALL_ERROR;} //安装成功,返回INSTALL_SUCCESSreturn INSTALL_SUCCESS;}

4、调用finish_recovery方法,清除BCB信息,重启

OTA升级成功,清空misc分区(BCB置零),并将保存到内存系统的升级日志/tmp/recovery.log保存到/cache/recovery/last_log。重启设备进入MainSystem,升级完成。

结束语

以上就是对升级流程的分析,希望对大家有所帮助。后续我们还会带来系统升级流程相关的介绍,感兴趣的同学可以关注我们的 同名微信公众号“麻辣软硬件”,与我们一起交流相关专业知识。

干货贴:如何设置灵活可配置的安卓升级机制?

安卓App升级机制该如何设计才能灵活可配置?作者对此分享了几点看法,一起来学习下。

为什么要做“非传统的”升级提示框

如果是经常用苹果手机的用户,app通常是自动升级没什么感知,而且本身AppStore原则上并不允许App弹出升级提示框,代码里检测出对应代码审核会不容易通过的。

Android用户会比较多见升级提示,总希望用户可以去升级新的版本,传统升级提示框都比较简单,复古的弹框加居右的“马上升级”“取消”,满足升级基本需求。为什么要做升级提示框呢,有以下原因:

好看又好用的升级提示框哪个产品不想拥有呢

对于tob产品,每个迭代都在接近用户走进用户,了解用户使用场景和功能业务需求,好不容易满足部分用户的痛点需求,当然会线上线下哭着喊着请求用户升级,希望用户赶紧使用新的功能,听听用户的反馈,产品经理时刻关注线上版本分布的数据,获取第一手数据资料,关注升级情况。

通常越是企业级的用户升级惰性是很大的,升级则意味着新功能,新的功能意味着企业的培训成本,尤其是对信息化程度较为重视的企业,互联网化的tob产品的互联网式升级就意味着更多的人力成本和学习成本的付出(当然这里还会引出企业级产品线下服务的重要性,后话,以后再讲)

而to C产品抓住用户个体的欲望,带着情怀带着实用带着双十一双十二双十三的实际诱惑就引得用户升级了。

小的功能完善和bug修复安卓发布热更新版本就可以了,但是大的功能版本升级,总是希望用最好的方式打动用户去升级,希望即使用户不升级,也能阅读升级提示框的一两行文案,记得我们更新了什么,回头使用过程中发现痛点了,能想起来,啊我可以更新试试看。

好的升级提示框多少也有产品经理的私心,PM总希望能把升级提示框做的颇有特色,网易云音乐的升级弹框符合其产品主元素的设计且体现其情怀,有所设计的升级提示在阅读节奏上带给用户很好的体验,能简明扼要的告知用户升级可以获得哪些新体验新功能。

于是我们在设计提示框时,也约定俗称的设置:标题 升级重点功能概述(不超过5个,基本遵循用户阅读习惯和”3-5“交互原则) 升级按钮。

为何要做的灵活可配置

灵活可配置的设想基于两个方面:一不依赖终端发版去调整,二可扩展运营动作(这个框可以不止用来升级)。

基于服务端搭建配置平台,可自由配置提示升级提醒方式,升级标题内容等样式配置,按钮数量和逻辑配置等等。

以下为关于服务端配置平台的内容思考过程:

提醒方式

产品层面可以采用以下几种升级提示方式,都比较常用

  • 弹框
  • H5的蒙层
  • 通知栏
  • 小红点
  • 通过产品内的”XX助手””XX团队”去推送新功能介绍

升级弹框设计

在服务端配置3个操作按钮:立即升级/稍后再说/跳过此版本 至多三个,多了也没必要了。

根据上述level在服务端配置决定显示哪些按钮。

包括强制升级(此时下载过程中不能让对话框消失,甚至可以设计在线安装的进度条)

  1. 立即升级
  2. 稍后升级,可在服务端进行持续重复提醒的逻辑,例如12小时之后??第二天再提示?等
  3. 跳过此版本

思考升级推送的用户范围和维度:哪些企业?哪些用户?哪些版本?哪些类型的企业?

  1. 针对用户当前版本升级
  2. 比如针对特定版本升级
  3. 针对特定企业升级
  4. 灰度
  5. 针对特定机型升级,在登录接口里传给服务器(根据线上数据关注用户中的主流机型)

  6. 华为

  7. 小米
  8. vivo
  9. oppo
  10. 魅族
实战分享:配置平台整体方案

服务端控制机制

(注*即服务端需要进行控制的,可以在服务端进行随时调整,解放终端发版压力)

升级文案富文本: 标题 内容

最终我选择了2个button的方案

  • button1文案:填写(注*方便button文案的调整)
  • button2文案:填写(注*方便button文案的调整)

规则可以叠加使用,选择完毕后,确定生效,并记录操作日志。

终端对应展现

默认安卓端用原生弹框提示升级,属基础模式,由服务端下发标题和内容及button的文案,字体字色字号由终端渲染。

服务端可调用自定义H5模式提示升级,属高级模式,由服务端给终端URL,终端利用JSAPI渲染调用。

终端:点击 立即升级去往AppStore或者直接下载,选择 稍后再说则暂时关闭弹窗,48小时后再次进入app时弹窗提示升级,N次(可酌情设置)点击稍后再说之后则不再提醒该用户该版本的升级,次数由服务端控制;

通知栏模式大家应该比较好理解,我就不再赘述了,描述文案可开放脑洞;

在关于中,增加新版本检测功能,有新版本检测时,可以显示“发现新版本”,然后点击弹窗“当前版本为XXX,是否升级更新至XXX版本”,点击马上升级去往AppStore或者直接下载,选择暂不升级则放弃本次升级。

未来你可以将这个部分作为运营平台的雏形,陆续扩展其在终端的线上运营动作配合。

  • 比如用这个框来做功能推荐,button就可以设置为“了解更多”,然后嵌一个H5的链接进去 ,做更多介绍的页面;
  • 再比如,可以在整个app,将红点引导的配置全都丢给服务端,在每个版本的迭代中,做功能点击引导,甚至可以按照角色去做不同配置,灵活多变。

从此你就拥有了一个智能运营管理平台。

本文由 @ShirleyW 原创发布于人人都是产品经理。未经许可,禁止转载。

安卓app升级的3种方式及交互细节

app升级是一个看似简单其实很复杂的事情;网页可以随时更新,用户不需要做任何操作就可以升级到最新版本;但app每次版本发布都需要用户配合才能完成。如何在不同的网络环境下,尽量不打扰用户,让更多的用户完成版本升级?我们以几个电商app为例,来研究下他们是怎么做的。

一、WIFI环境(WLAN)

1. 静默升级、自动下载、提示升级

如图14-1,京东、天猫、苏宁正好代表了目前主要的3种升级方式。

京东采用的是静默升级,WIFI环境下进入应用,不弹出升级提示,直接在后台下载完成升级,升级过程中用户没有任何感知(注:图片中的提示,是点击检查更新后弹出的)。

天猫采用的是WIFI下自动下载,开始也是跟京东一样,进入后不弹出提示直接下载,不同的是在下载完成后,立即弹出提示,告诉用户已经下载完成,问用户是否安装。这种升级方式需要用户手动安装,并且用户在使用过程中弹出提示,会打扰用户,显然没有静默升级体验好。

苏宁则是提示升级,在进入应用后,立即弹出升级提示,并提示当前是WIFI环境。

图14-1 WIFI环境下升级(图片来源:京东、天猫、苏宁)

2. 自动下载完成后的提示

京东的早期版本,也做过类似天猫的自动下载升级,如图14-2所示。

下载完成后,告诉用户新版本做了哪些更新,有多少比例的用户已经升级了,更着重强调“无需流量”,对比图14-1天猫的提示,显然引导效果要好很多。

图14-2 京东早期版本自动下载升级提示(图片来源:京东)

3. 自动升级开关

从图14-3可以看到,京东、天猫在设置里面有WIFI环境下自动升级的开关,默认是打开的,苏宁没有类似功能。所以在WIFI情况下打开京东、天猫,程序在后台已经开始下载升级包,而没有弹出升级提示。

另外我们注意到,该功能设置不需要用户登录,说明该功能不是针对账号,而是针对手机的,也就是说,同一个用户在不同的手机上使用产品,要分别进行升级,这样的设置显然是非常合理的。

图14-3 自动下载开关(图片来源:京东、天猫)

二、4G环境(移动数据)

1. 红点引导

几个平台升级提示的红点引导,总体来说强度适中,对于有强迫症的用户来说,是非常友好的。

从图14-4可以看到,京东的红点提示是从账户页面右上角的设置图标开始的,一直到关于页面结束(中间省略了部分页面),用户只有进入到账户页面才能看到红点,而不是像有些app,红点直接出现在菜单栏的账户图标上,用户打开app进入首页就看到红点提示,那样提示的强度就高了很多。

从图14-5可以看到,苏宁没有做任何红点引导,用户只有到了关于页面,才可以看到灰色字体显示的发现新版本。

图14-4 升级提示红点引导(图片来源:京东)

图14-5 升级提示引导(图片来源:苏宁)

2. 下载过程

如图14-6所示,当用户在升级提示框点击下载按钮后,京东、苏宁进入下载过程,下载完成后京东弹出安装提示,苏宁直接进入安装页面。

点击下载按钮后,如果在手机状态栏设置了流量显示,用户可以通过流量变化知道是否正在下载,如果没设置显示流量,则对用户来说没有任何变化,用户不知道是否已经在下载。

这里可以增加相应的用户反馈,比如在点击下载按钮后,toast提示升级包正在下载,或者通过进度条来显示下载进度。另外可以考虑在升级提示框增加显示升级包大小,用户可以据此判断是否通过4G网络下载。

图14-6 下载完成(图片来源:京东、nubia UI)

三、流程图

通过以上分析,我们得到京东、天猫、苏宁下载的流程图,如图14-7:

图14-7 京东、天猫、苏宁下载流程图

四、灰度发布

除了常见的按用户群、版本号等灰度发布之外,在更新顺序上,如图14-8所示,应用市场已经有了最新版本可以更新,但是进入京东、天猫app并没有弹出升级提示,京东可以通过14-4的红点引导进入关于页面升级,而天猫则显示已是最新版本,也就是说天猫还不可以本地升级。

所以可以推测更新的顺序是,先更新应用市场,再红点提示更新,最后再弹框提示,也就是升级的用户量是由少到多的顺序。

图14-8 灰度发布(图片来源:360手机助手、天猫)

总结

本文以京东、天猫、苏宁为例,分析了安卓app在WIFI、4G网络下,静默升级、自动下载、提示升级3种主要的升级方式,以及红点引导、下载过程、灰度发布等相关的交互细节,希望给安卓app的小伙伴一些参考。

张旭东,微信公众号:旭东爱折腾,人人都是产品经理专栏作家。努比亚手机商城产品经理,前华强旗舰店产品经理,FON乐队吉他手。

本文原创发布于人人都是产品经理。未经许可,禁止转载

题图来自Unsplash,基于CC0协议

大家都在看
本文经用户投稿或网站收集转载,如有侵权请联系本站。