背景
旧车机系统升级都是通过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升级
工作流程
应用层面:
系统层面:
进入Recovery模式后,流程图如下:
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流程:
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=指定生成目录
得到编译包文件,设文件名为{用户名称}.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模式分区对比
分区对比
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 //将第一个槽设置为下次启动生效
详细用法:
系统分区属性
每个slot都有自己的系统分区属性,主要有:
active
系统的活动分区标识,系统只能有一个分区设置为active属性,启动时bootloader选取设置为active的分区进行启动。
bootable
分区可启动标识,设置为bootable的分区表明该分区包含了一个完整的可以启动的系统。
successful
分区成功运行标识,设置为successful的分区表明该分区在上一次启动或当前启动中可以正确运行。
retry count
分区可尝试重启的次数
主要有这几个状态:
bootloader会去读取boot_control私有的存储数slot metadata并进行解析得到上述active、bootable、successful三个属性,以此确定从哪一个slot启动。
AB启动流程
整体流程图:
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=指定生成目录
得到编译包文件,文件名为{用户名称}.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:
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/目录下,服务会定时检测升级包并自动弹出升级对话框:
点击安装后将自动安装,安装完毕将自动重启
安装方式二:
同全量升级方式使用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的计算方式:
绑定回调:
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等打包到这个分区中:
并引入描述分区布局的metadata(元数据),结构如下:
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需要两次写操作,一次写旧数据,一次写新数据。
ROW (Redirect-On-Write)写时重定向,创建快照写入新数据时,原来设备中的数据不变,新数据写入到快照设备中,所以ROW只需要一次写新数据的操作。
由于升级时主要都是写操作,所以快照基本上都是使用 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) 来进行格式转换:
虚拟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升级一致
方案对比
google官方给的内存使用情况:
最终方案选定
按发展趋势来说,虚拟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文件提供给设备下载。
从文件大小看,差分包的大小比两个apk的差值还小。
2、利用差分包合成升级包apk
在android代码中导入bsdiff差分代码
在项目的build.gradle的android{}块中添加CmakdeList.txt的引用
android{
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.22.1"
}
}
}
java native需要引用的是native-lib.cpp的方法:
框选部分为我们自定义的包名+类名+方法名,例如引用的地方是com.wisee.update.bsdiff.PatchUtil#bsCompound(),
所以native-lib.cpp的方法名就是Java_com_wisee_upgrade_bsdiff_PatchUtil_bsCompound()
三个参数依次是旧apk路径,差分包路径,要生成的新包路径。
3、得到新包路径output之后即可安装升级。