固件系统升级和apk差分升级方案

1,149 阅读16分钟

背景

旧车机系统升级都是通过4s店进行系统升级,费时费力,目前主流车机系统大部分都支持OTA升级,不需要到店升级,省去了人工成本,是车机必备的重要功能。

需求

需要实现固件/应用的全量/差分升级和本地/OTA升级。

升级方式全量升级差分升级
OTA升级固件:从云端下载完整固件包到设备进行升级应用:从云端下载apk到设备进行升级固件:从云端下载差分包到设备进行升级应用:从云端下载apk差分包到设备进行升级
本地升级固件:将完整固件包放到U盘插入设备升级应用:将apk包放到U盘插入设备升级固件:将差分包放到U盘插入设备升级应用:将apk差分包放到U盘插入设备升级
为什么要实现差分升级?

由于车机大部分使用的是移动数据流量,每个完整固件包的大小基本都要几百M甚至1~2G,如果每次升级都使用全量升级,会导致耗费大量的流量,使用差分升级能够大大减少所需要的流量。

现有方案

名词解释

OTA:空中下载技术(Over-the-Air Technology)通过移动通信的空中接口实现对移动终端设备进行远程管理的技术。

差分升级:基于设备某个版本作为基线版本,然后将修改后的版本与基线做差分计算得到差分升级包进行升级,优点是升级包体积小,OTA下载消耗流量小,安装速度快,缺点是多一步差分计算才能得到升级包,且不同基线版本无法升级。

BSDiff:是一种二进制文件差分算法,在软件更新、补丁分发等领域非常有用,因为它生成的差分文件通常比其他差分算法生成的文件更小。

Recovery升级:android7.0以前的升级方式,主要通过Recovery模式升级。

Recovery模式:恢复模式,android三种启动模式之一(Fastboot,Recovery System 以及Main System),进入方法:reboot recovery。

A/B升级:谷歌官网叫无缝更新,是自android7.0开始新增的一种android设备升级方式,通过引入两个物理分区来实现一个分区运行主系统,另一个分区无感升级的功能。

虚拟 A/B升级:虚拟 A/B (Virtual A/B)是 Google 在 Android 8.0(Oreo)引入的一种更新机制。通过动态分区+快照+ROW写时复制等技术,拥有传统 A/B 分区的无缝更新的优势,并且相比传统A/B分区更省空间。

虚拟 A/B 压缩升级:在虚拟A/B的基础上通过对快照进行XOR异或压缩,对内存空间的需求进一步节省。

全量升级:指将整个升级包文件导入设备升级,优点是不需要基于任何版本就可以升级,缺点是升级包过大时在OTA下载时会消耗较多流量,且安装速度较慢。

Misc分区:一个非常小的分区,4 MB左右。recovery用这个分区来保存一些关于升级的信息(启动模式、更新状态、升级包路径等),应对升级过程中的设备掉电重启的状况,Bootloader启动的时候,会读取这个分区里面的信息,以决定系统是否进Recovery System 或 Main System。

BCB:Bootloader Control Block,启动控制信息块,位于misc分区,是Recovery与Bootloader通信获取启动参数的模块。

Cache:系统缓存区,临时的保存应用数据, OTA升级过程会清除这个分区的数据,Android差分包升级也需要依赖此分区存放一些中间文件。

一、固件升级

目前市场上的升级方案主要有Recovery升级、A/B升级、虚拟A/B升级、虚拟A/B压缩升级。

1、Recovery升级

工作流程

应用层面:

image.png

系统层面:

进入Recovery模式后,流程图如下:

image.png BCB解析:

描述misc分区的数据结构是bootloader_message

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

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

boot-recovery:系统将启动进入Recovery模式

update-radia 或者 update-hboot:系统将启动进入更新firmware的模式,这个更新过程由bootloader完成

NULL:空值,系统将启动进入Main System主系统,正常启动。

reboot recovery或reboot bootloader就是修改这个值来引导系统进入Recovery模式或者Fastboot模式。

status 字段

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

recovery 字段

存放的是recovry模块的启动参数,一般包括升级包路径。其存储结构如下:

第一行存放字符串“recovery”,第二行存放路径信息,如“–update_package=/mnt/sdcard/update.zip”等。 因此,参数之间是以“\n”分割的。

get_args流程:

image.png

1、get_args

先判断启动Recovery的命令里是否带有参数(也就是get_args函数的参数),有就读参数,否则调用get_bootloader_message读取misc分区的recovery,如果从/misc分区中也没有读到命令,就读取/cache/recovery/command的参数,最后调用set_bootloader_message保存到了/misc分区中防止掉电丢失。

2、update_package

通过update_package方法来判断update.zip是否存在(一般存放在/mnt/sdcard/或/cache/recovery/command内)来决定是否要安装或擦除缓存。

3、really_install_package->verify_file

调用ensure_path_mounted校验SF文件与RSA文件是否匹配、MANIFEST.MF与签名文件中的digest是否一致、包中的文件与MANIFEST中所描述的是否一致

4、try_update_binary

校验成功之后执行try_update_binary开始更新。

升级步骤如下:

1)判断是不是升级包是否适用于该设备,如果不适用,则停止升级,否则继续。

2)显示进度条

3)格式化system分区

4)挂载system分区

5)将ota升级包里面的system目录解压到system分区

6)建立一些软链接,升级过程需要用到

7)设置部分文件权限

8)将升级包里面的boot.img写入到/boot分区

9)清空misc分区,即BCB块置为NULL

10)卸载system分区

4、升级成功后wipe data/cache擦除data/cache分区

5、prompt_and_wait

这个函数的作用就是一直在等待用户输入,是一个不断的循环,可以选择Recovery模式下的一些选项进行操作,包括恢复出厂设置和重启等。如果升级失败, prompt_and_wait会显示错误,并等待用户响应。

6、finish_recovery

OTA升级成功,清空misc分区并重启设备进入Main System,升级完成。

Recovery 编译升级流程

1、编译系统文件

source build/envsetup.sh

lunch ${your version}

mkdir file_output

make -jx dist DIST_DIR=file_output

x表示参与编译的cpu核数 dist DIST_DIR=指定生成目录

得到编译包文件,设文件名为产品名称targetfiles{产品名称}-target_files-{用户名称}.zip

2、调用RecoverySystem.verifyPackage(File packageFile,ProgressListener listener,File deviceCertsZipFile)校验签名和证书。(这一步是可选的,因为进入Recovery模式还会再校验一遍)

3、调用RecoverySystem.installPackage(Context context, File packageFile)函数来发起安装过程,这个过程主要的原理,实际上是往 /cache/recovery/command 写入ota升级包存放路径,然后重启到recovery模式

RK Recovery 编译升级流程

1、全量编译升级包

make installclean && make -jx && make dist -jx && ./mkimage.sh ota 

或者

build.sh –AUCKuop

注:其中mkimage.sh和build.sh是RK自定义的脚本,不属于aosp自带的编译脚本。

在out/target/product/rkxxxx/目录下会生成ota完整包rkxxxx-ota-eng.root.zip,改成update.zip。

2、将update.zip拷贝到USB、SD卡根目录,或/data/media/0/ 目录下:

3、最后以recovery模式重启:

reboot recovery

重启后自动通过RK自带的升级服务RKUpdateService来升级,下面会介绍具体流程

2、A/B升级

特点:

1、出厂时设备上有两套可以正常工作的系统,升级时确保设备上始终有一个可以工作的系统,减少设备变砖的可能性,方便维修和售后。

2、OTA升级在Android系统的后台进行,所以更新过程中,用户可以正常使用设备,数据更新完成后,仅需要用户重启一次设备进入新系统

3、如果OTA升级失败,设备可以回退到升级前的旧系统,并且可以尝试再次更新升级。

工作原理

系统同时存在两套system分区和boot分区,一套处于休眠状态不可使用,一套处于使用状态,两者通过slot(插槽)标志位的概念来做区分,在设备启动引导阶段通过特殊标记位确定启动哪个system,

当有可用升级版本时候,客户端将升级包下载下来,然后修改启动标志位,在下次启动的时候,就进入升级到最新版本的那套system系统。

与Recovery模式分区对比

分区对比

image.png

boot_control

区别于Recovery方式读取存储于misc分区的bootloader_message函数,设备启动后bootloader会读取HAL层的boot_control私有实现的数据,来判断从哪一个slot插槽启动。

boot_control位于hardware/libhardware/include/hardware/boot_control.h,主要是一些启动状态和分区状态的读写。

boot_control相当于Java的Interface,需要去实现它定义的接口,这里由于各家实现的私有数据结构不一样,所以无法详细说明如何解析和处理的过程,有兴趣自行研读,主要有谷歌、英特尔、高通,还有其他芯片厂商也会自己定制实现boot_control的接口。

boot_control的调试工具 bootctl

AOSP也提供了boot_control模块调用的工具bootctl,位于:system/extras/bootctl/bootctl.c

常用命令示例:

bootctl get-current-slot  // 获取当前slot分区 返回分区的index,从0开始,以此类推

bootctl get-number-slots  //获取分区数

bootctl get-suffix 0//获取第一个slot分区的后缀,例如第一个分区是slot_a分区,则返回_a,以此类推

bootctl set-active-boot-slot 0 //将第一个槽设置为下次启动生效

详细用法:

blog.csdn.net/xiaowang_lj…

系统分区属性

每个slot都有自己的系统分区属性,主要有:

active

系统的活动分区标识,系统只能有一个分区设置为active属性,启动时bootloader选取设置为active的分区进行启动。

bootable

分区可启动标识,设置为bootable的分区表明该分区包含了一个完整的可以启动的系统。

successful

分区成功运行标识,设置为successful的分区表明该分区在上一次启动或当前启动中可以正确运行。

retry count

分区可尝试重启的次数

主要有这几个状态:

image.png bootloader会去读取boot_control私有的存储数slot metadata并进行解析得到上述active、bootable、successful三个属性,以此确定从哪一个slot启动。

AB启动流程

整体流程图:

image.png

1、系统启动后,bootloader读取分区插槽的元数据slot metadata;

2、检查元数据中是否有可启动的分区(即判断分区里至少有一个bootable为true),如果没有可启动分区,直接进入bootloader的recovery mode;

3、如果分区元数据中有可启动的分区,则选择所有可启动分区中优先级最高的slot(正常启动时就是选择状态为active的分区);

4、检查所选择分区的retry count(retry count表示当前分区可以尝试启动的次数);

5、如果当前选择分区的retry count为0,且没有启动成功(启动成功的分区会标记为successful),则将所选择分区标记为无效分区(设置bootable为false),然后重复第2步,查找下一个可以启动的分区;

6、如果当前选择的分区尝试启动次数retry count不为0,则表示还可以继续尝试从当前分区启动,retry count次数减1,然后加载相应的slot进行启动;

AB系统升级

A/B系统升级包的制作方式跟传统系统升级包制作方式基本一致,主要分为两步:

全量升级

BoardConfig

1、编译系统文件

source build/envsetup.sh

lunch ${your version}

mkdir file_output

make -jx dist DIST_DIR=file_output x表示参与编译的cpu核数 dist DIST_DIR=指定生成目录

得到编译包文件,文件名为产品名称targetfiles{产品名称}-target_files-{用户名称}.zip

2、制作升级包

把上一步得到的固件包作为input.zip,执行命令:

ota_from_target_files input.zip output.zip

例如 ota_from_target_files target_file.zip update.zip

update.zip内容如下:

执行 tree -l update.zip:

 |-- META-INF

 |   |-- CERT.RSA

 |   |-- CERT.SF

 |   |-- MANIFEST.MF

 |   `-- com

 |       `-- android

 |           |-- metadata

 |           `-- otacert

 |-- care_map.txt

 |-- payload.bin

 `-- payload_properties.txt

其中,payload.bin是系统要更新的数据文件,payload_properties.txt包含了升级内容的一些属性信息,如下:

 FILE_HASH=ozGgyQEcnkI5ZaX+Wbjo5I/PCR7PEZka9fGd0nWa+oY=

 FILE_SIZE=282164983

 METADATA_HASH=GLIKfE6KRwylWMHsNadG/Q8iy5f7ENWTatvMdBlpoPg=

 METADATA_SIZE=21023

升级时会使用到payload_properties.txt里面的信息。

3、安装升级包

升级工具客户端update_engine_client

调用update_engine_client --update进行升级,必带--payload和–header参数:

update_engine_client \

 --payload=file:///${path}/update.zip \  #可以使用http/https方式

 --update \

--offset=2559 \

--size=600199037 \

 --headers="

   FILE_HASH=ozGgyQEcnkI5ZaX+Wbjo5I/PCR7PEZka9fGd0nWa+oY= 

   FILE_SIZE=282164983

   METADATA_HASH=GLIKfE6KRwylWMHsNadG/Q8iy5f7ENWTatvMdBlpoPg= 

   METADATA_SIZE=21023 

 "

或者

update_engine_client \

 --payload=${path}/payload.bin \  #可以使用http/https方式

 --update \

 --headers="

   FILE_HASH=ozGgyQEcnkI5ZaX+Wbjo5I/PCR7PEZka9fGd0nWa+oY= 

   FILE_SIZE=282164983

   METADATA_HASH=GLIKfE6KRwylWMHsNadG/Q8iy5f7ENWTatvMdBlpoPg= 

   METADATA_SIZE=21023 

 "

安装结束后查看日志:logcat -s update_engine:v

update_engine更新操作成功后会提示Update successfully applied, waiting to reboot.

注意点1:这里update_engine_client的--headers=参数最终是按行进行拆分提取的,所以需要将--headers=的每个参数分别写到一行上,然后全部参数用双引号包含,否则可能会出现无法正常解析headers参数导致无法正常执行的情况。

注意点2:如果使用update.zip来升级,需要传入--offset和--size,即payload.bin在update.zip中的偏移量和大小,下文介绍UpdateEngine时会讲如何计算偏移量和大小。

RK AB升级配置

1、环境配置

device\rockchip\rkxxx\BoardConfig.mk中配置:

BOARD_USES_AB_IMAGE := true

在对应的device\rockchip\rkxxx目录下,确认是否已经有recovery.fstab_AB文件,如果已有,则直

接跳过这一步;如果没有recovery.fstab_AB文件,则按如下步骤执行:

1)新增针对AB的recovery fstab文件recovery.fstab_AB

与对应的fstab.rk30board的主要区别在于AB分区增加slotselect挂载参数(system/vendor/odm/product增加

slotselect参数),同时将exernal_sd, frp, parameter, baseparameter, resource的分区节点添加进去,并更改

data区的挂载方式选项。

一个参考文件如下:

device\rockchip\rk3399\rk3399_Android12\recovery.fstab_AB:

image.png 2)device\rockchip\rkxxx下的BoardConfig中导入AB配置,包括TARGET_RECOVERY_FSTAB,使其

指向刚刚创建的recovery.fstab_AB文件。

RK AB全量升级

1、编译AB全量升级包

lunch xxx

make installclean –jx  或者 make clean -jx

make –jx

make dist –jx

mkimage_ab.sh  ota

注意:开启AB后,第一次需要make clean后再编译。

在out/target/product/${产品名称}/目录下会生成ota完整包形如xxx-ota-eng.xxx.zip

2、安装全量包

安装方式一:

利用RKUpdateService服务进行升级

将上述升级包重新命名为update.zip,并拷贝至/data/media/0/目录下,服务会定时检测升级包并自动弹出升级对话框:

image.png 点击安装后将自动安装,安装完毕将自动重启

安装方式二:

同全量升级方式使用update_engine_client

AB差分升级

差分升级包的制作

首先需要有当前版本的和新版本的固件包,制作方法同上面全量升级包,设旧包为target_file-old.zip,新包为target_file-new.zip

执行命令 ota_from_target_files -i target_file-old.zip target_file-new.zip patch.zip  //这里通过-i指定差分包生成的基线

安装方式

同样是调用update_engine_client,参数使用patch.zip的payload.bin和payload_properties.txt

RK AB差分升级

1、编译升级包

同全量编译

2、保存out/target/product/rkxxxx/obj/PACKAGING/target_files_intermediates/rkxxx-target_files-

eng.xxx.zip 为rkxxxx-target_files-v1.zip,作为v1版本的基础素材包。

3、修改kernel代码或者android 代码,发布v2版本固件,生成v2版本完整包。编译命令和1.相同;

4、保存

out/target/product/rkxxxx/obj/PACKAGING/target_files_intermediates/rkxxx-target_files-eng.xxx.zip 为

rkxxxx-target_files-v2.zip,作为v2版本的基础素材包。

5、生成v1-v2的差异升级包。参考命令如下:

./build/tools/releasetools/ota_from_target_files -v -i ./rockdev/v1/rk3588_t-

target_files-eng.jdy.zip --block -p ./out/host/linux-x86 ./rockdev/v2/rk3588_t-

target_files-eng.jdy.zip ./rockdev/v1v2.zip

得到的./rockdev/v1v2.zip即为差分包

2、安装升级包

安装方式一:

使用update_engine_client进行升级,方式同AB差分升级

安装方式二:

update_device.py脚本升级,执行:

system/update_engine/scripts/update_device.py {升级包完整路径}

注:其中mkimage_ab.sh和update_device.py是RK自定义的脚本,实际也是通过调用update_engine_client来实现升级。

UpdateEngine

UpdateEngine是应用端与update_engine_client和update_engine通过AIDL的方式通信的实现类,通过原生UpdateEngine#applyPayload(String url, long offset, long size, String[] headerKeyValuePairs)实现A/B升级或虚拟A/B升级。

参数解释:

url:文件路径

offset:payload.bin在文件中的起始位置偏移量,用于确定读取的位置

size:文件大小,用于确定读取的长度

headerKeyValuePairs:升级包里payload_properties.txt文件的键值对

对于 payload.bin 文件,因为是原始的 payload 文件,所以 offset 为 0, size 为整个 payload.bin 文件的大小,如果没有提供 size, 默认就读取文件直到文件结束。

对于 update.zip 文件,里面包含了压缩的 payload.bin 文件,因为是压缩过的 payload,所以为了读取完整的 payload,必须提供 payload 在 zip 包的偏移位置,以及 payload 在 zip 包压缩后的数据大小。

从 Android 8.0 (O) 开始,制作升级包时,会自动计算 payload 的 offset 和 size 并输出到 zip 包的 META-INF/com/android/metadata 文件中,其中名为ota-streaming-property-files的键值对记录了payload.bin的offset和size,

形如:

ota-streaming-property-files=payload.bin:738:271041806,payload_properties.txt:271042602:154,care_map.txt:474:217,metadata:69:357

其中738就是payload.bin的offset,271041806是payload.bin的size。

offset和size的计算方式:

blog.csdn.net/guyongqiang…

绑定回调:

UpdateEngine#bind(UdateEngineCallback callback, Handler handler)

解绑回调:

UpdateEngine#unbind()

取消升级:

UpdateEngine#cancel()

3、虚拟A/B升级

工作原理

利用了动态分区(Dynamic Partitions)和快照擦写分区(Snapshot/Copy-on-write,简称 COW)技术。

动态分区

Android 从 Q 10 开始引入动态分区 super,将原来的system_a,system_b,vendor_a,vendor_b等打包到这个分区中:

image.png 并引入描述分区布局的metadata(元数据),结构如下:

image.png Reserved:

预留和备用空间,保留一定空间,为后续升级提供兼容性和灵活性。

Geometry:

描述存储设备上分区布局的结构,定义分区的起始位置、大小以及其他物理属性。它可以看作是存储设备的“地图”,告诉系统每个分区在哪里开始、结束以及它们的大小。

Metadata:

每个slot都对应两个Metadata,一个Primary,一个Backup,Primary用于反映当前系统的分区布局,Backup用于OTA升级时写入新系统的分区布局。

每一个Metadata都有自己的逻辑分区结构体(Lpxxxx结构体,L Logic P Partition,即逻辑分区缩写),

主要描述逻辑分区的映射关系,内容较多不展开讲,有兴趣自行研读。

Snapshot快照+ROW 写时重定向

从快照( snapshot) 原理的角度来讲,一共分 3 种:

Full Volume Copy全卷复制,就是对整个存储卷的内容完全进行 copy,这种方式相当于一个完全备份。

COW (Copy-On-Write)写时复制,创建快照写入新数据块时,将原来设备中的数据 Copy 到快照设备中,然后将新数据写入到原来数据块的地方,所以COW需要两次写操作,一次写旧数据,一次写新数据。

image.png ROW (Redirect-On-Write)写时重定向,创建快照写入新数据时,原来设备中的数据不变,新数据写入到快照设备中,所以ROW只需要一次写新数据的操作。

image.png 由于升级时主要都是写操作,所以快照基本上都是使用 ROW 的方式可以提高IO效率,即新数据写入到快照设备中,旧数据在源卷中维持不变,然后再通过merge的操作,将快照中的新数据写入覆盖原始设备中的数据。

升级流程

1)环境配置

在设备的BoardConfig.mk文件中添加以下配置: 

BOARD_USES_DYNAMIC_PARTITIONS := true

TARGET_USE_VIRTUAL_AB := true 

定义虚拟A/B升级需要的分区 ,如配置system vendor boot分区:

AB_OTA_PARTITIONS := system vendor boot

2)编译流程

同A/B升级,原理都是利用update_engine_client和update_engine实现

RK 虚拟AB升级流程

1)环境配置

device\rockchip\rkxxx\BoardConfig.mk中配置:

BOARD_ROCKCHIP_VIRTUAL_AB_ENABLE := true

BOARD_USES_AB_IMAGE := true

2)编译流程

同RK A/B升级流程

4、虚拟A/B压缩升级

工作原理

android12及以上开始支持虚拟A/B压缩升级,提供gz, lz4, zstd, and none多种压缩方式,通过dm-user创建用户态进程来处理压缩、解压缩、加密、解密快照等IO操作,并将这些I/O处理逻辑转移到用户空间(为了简化内核设计,提高内核的安全性和稳定性)。

内核和用户空间之间的COW在snapuserd(Snapshot User Daemon) 来进行格式转换:

image.png 虚拟AB压缩使用的是XOR异或压缩,即相同为0,不同为1,所以在改动较小时可以大幅减少数据量。

升级流程

同样在BoardConfig.mk文件中添加以下配置:

BOARD_USES_COMPRESSED_VIRTUAL_AB := true

使用brotli压缩工具

PRODUCT_USE_BROTLI := true

其他同虚拟AB升级

RK 虚拟AB升级流程

1)环境配置

device\rockchip\rkxxx\BoardConfig.mk中配置:

BOARD_ROCKCHIP_VIRTUAL_AB_ENABLE := true

BOARD_USES_AB_IMAGE := true

BOARD_ROCKCHIP_VIRTUAL_AB_COMPRESSION := true

其他和A/B升级一致

方案对比

image.png google官方给的内存使用情况:

image.png

最终方案选定

按发展趋势来说,虚拟AB压缩>虚拟AB>传统AB>Recovery,但由于使用的是RK的芯片,文档提示RK的虚拟AB和虚拟AB压缩有性能问题,且为了兼顾稳定性和维护成本,选择传统AB升级方案。

二、APK升级

1、全量升级

将apk下载到本地目录下即可调用java方法进行安装:

private void startInstallApk(File file) {
    try {
        Process process = Runtime.getRuntime().exec(new String[]{"pm", "install", "-r", file.getPath()});
        process.waitFor();
        if (process.exitValue() == 0) {
            for (IUpdateCallback callback : mCallbacks) {
                callback.onUpdateSuccess();
            }
        } else {
            for (IUpdateCallback callback : mCallbacks) {
                callback.onUpdateError("apkPatchUpdate start error");
            }
        }
    } catch (Exception e) {
        LogUtil.e(TAG, "startInstallApk error " + e.getMessage());
    }
}

2、差分升级

整体流程

云端利用BSDiff差分算法,根据新旧apk得出差分包,设备通过ota方式下载得到差分包后,调用c++方法(同样使用BSDiff)将差分包和旧包合成升级包再进行安装升级。

升级步骤

1、制作差分包

准备当前设备的应用apk和需要升级的应用apk,使用BSDiff差分算法脚本得到差分包patch文件(代码小改动时一般文件大小为几十K)。此处以windows系统为例。 官方网址:http://www.daemonology.net/bsdiff/

命令为:xxx/bsdiff.exe xxx/old.apk xxx/new.apk xxx/patch

顺序为bsdiff脚本文件路径 旧apk路径 新apk路径 差分包目标路径

例如:

C:\Users\DELL\Desktop\bsdiff_win_exe\bsdiff.exe C:\Users\DELL\Desktop\bsdiff_win_exe\WiseeSettings-old.apk C:\Users\DELL\Desktop\bsdiff_win_exe\WiseeSettings-new.apk C:\Users\DELL\Desktop\bsdiff_win_exe\patch

得到差分包之后,通过OTA的方式,将patch文件提供给设备下载。

image.png 从文件大小看,差分包的大小比两个apk的差值还小。

2、利用差分包合成升级包apk

在android代码中导入bsdiff差分代码

image.png 在项目的build.gradle的android{}块中添加CmakdeList.txt的引用

android{    
    externalNativeBuild {
        cmake {
         path "src/main/cpp/CMakeLists.txt"
         version "3.22.1"
        }
    }
}

java native需要引用的是native-lib.cpp的方法:

image.png 框选部分为我们自定义的包名+类名+方法名,例如引用的地方是com.wisee.update.bsdiff.PatchUtil#bsCompound(),

所以native-lib.cpp的方法名就是Java_com_wisee_upgrade_bsdiff_PatchUtil_bsCompound()

image.png 三个参数依次是旧apk路径,差分包路径,要生成的新包路径。

3、得到新包路径output之后即可安装升级。

参考文档: blog.csdn.net/guyongqiang…