• This repository has been archived on 08/Jun/2024
  • Stars
    star
    189
  • Rank 204,649 (Top 5 %)
  • Language
    C++
  • License
    MIT License
  • Created over 5 years ago
  • Updated 12 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

I no longer install and use Unity3D. (Unity3D热更新。provide a way to hot update Unity app on Android, support code&resources, not need lua js or IL runtime etc..., will not disturb your project development; just loading the new version apk file to achieve. )

UnityAndroidHotUpdate

release tag license +issue Welcome

[README English]
提供了一个在Android上热更新Unity开发的app的方案,支持代码和资源;不依赖其他语言(lua,js等)不干涉项目开发过程;它通过直接加载新版本apk文件来实现的。
( 依赖的库 ApkDiffPatch, xHook. )

该方案的效果

Android用户在使用app过程中,更新逻辑可以在后台完成下载新版本的任务,当app重新启动后用户看到的是新版本的app(不需要系统安装); 这里的“热更”就是指不需要经过系统的重新安装就拥有了新版本!

热更原理

Android程序在安装后,apk文件存放在getApplicationContext().getPackageResourcePath();并将其中相应abi的库文件解压存放到了getApplicationContext().getApplicationInfo().nativeLibraryDir,其中包括libmain.so,libunity.so,libil2cpp.so or libmono*.so,等;
本方案会在app运行中,将更新到的新版本apk存放到getApplicationContext().getFilesDir().getPath()的更新路径,并将apk中本机abi并发生了改变的库文件解压到更新路径; 当app重启后用hook和java层代码配合,将unity相关库访问apk文件和lib库文件路径的c文件访问函数映射到访问我们准备好的新的apk文件和lib路径。
( Hook Unity库的C文件API来实现热更的思路来源于UnityAndroidIl2cppPatchDemo,感谢,这里做了一些简化和改进。 )

特性

  • 简单快速
    原理和实现简单,并且支持代码和资源的热更新;同时支持il2cpp与mono这2种代码打包方式,支持libunity.so、libmono.so等库文件变动,支持libmain.so的RELEASE小版本改动,不支持Unity大版本(MAJOR和MINOR版本)变动; 接入项目简单,容易修改定制;(本文后面有接入项目说明)
    热更新后app执行速度不受影响,不像使用插件化方案后遇到的速度慢和Activity兼容等问题;
    这是一个几乎不改变Unity开发流程的方案,不需要引入额外的语言(如lua或js等),也不需要一个额外的中间层解释器(如ILRuntime)、动态加载反射、手工绑定等工作。
  • 下载流量小
    不需要下载完整的apk文件,而只需要下载已有版本和最新版本之间的差异就可以了;然后在用户本地合成新的apk文件;
    这里选择用的ApkDiffPatch方案,可以生成非常小的补丁;比如只是一些简单代码修改,补丁一般在百k级别。
  • 运行环境和兼容性
    在Unity5.6、Unity2017、Unity2018、Unity2019的多个版本上测试过;
    测试过分别使用mono代码后端和il2cpp代码后端;
    测试过armeabi-v7a、arm64-v8a和x86的设备;
    一般支持Andorid4.1及以上的系统了;但使用Unity5.6或Unity2017和il2cpp代码后端时可能只支持Andorid5及以上,你需要测试;
    在发布使用的Unity大版本(MAJOR & MINOR)没有变化(jni库的API不变)和没有新增.so库文件的情况下,一般都可以兼容;
    项目的常规.so库可以添加到允许更新的库列表中提前加载从而兼容热更(在HotUnity.java文件中添加,参见接入项目说明);
    测试过用相同的Unity大版本新建了一个最简单的app,也可以更新到一个已有的复杂的游戏app;
    当然某些涉及权限、特殊第三方业务等是否兼容还需要更多测试。

如何接入你的项目测试

  • 导出项目: 将你的Unity项目选择Gradle导出项目,以便修改后交给Android Studio进行打包;
  • 修改导出的项目:
    将libhotunity.so文件(注意abi路径对应,build项目在project_hook_unity_jni目录里)复制到项目的jniLibs下的相应子目录中;
    com/github/sisong/HotUnity.java文件复制到项目源代码的java相应路径中; (可以在这个文件中添加你需要支持热更的.so,这会立即加载可能存在的新版本库)
    在项目的UnityPlayerActivity.java文件中import com.github.sisong.HotUnity;,并且在mUnityPlayer = new UnityPlayer(this);代码之前添加代码HotUnity.hotUnity(this);
  • 如果你需要支持升级发布用的Unity的小版本,那么需要用FixUnityJar程序(代码在project_fix_unity_jar/fix_unity_jar路径里)修改导出项目中文件unity-classes.jar,并且将libnull.so文件(build项目在project_fix_unity_jar/null_lib目录里)复制到项目的jniLibs下的相应子目录中;
  • 为了兼容Android10安装APK,需要: 在安卓工程的AndroidManifest.xml文件的application节点中添加
<!-- if androidx: provider android:name="androidx.core.content.FileProvider" -->
<provider 
android:name="android.support.v4.content.FileProvider" android:authorities="{YourAppPackageName}.fileprovider" android:exported="false" android:grantUriPermissions="true">
      <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/update_files" />
</provider>

在安卓工程的res目录创建/xml/update_files.xml文件,内容为:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external" path="."/>
</paths>
  • 用Android Studio打包测试项目(你可以把这个导出、修改、打包的过程在Unity中利用编辑器扩展自动化下来;后续本仓库会更新到Demo中),app在设备上应该能够正常运行; 现在你需要测试热更新到一个新版本;
  • 手工热更新测试过程:假设有了修改过的新版本apk命名成update.apk,放置到getApplicationContext().getFilesDir().getPath()目录的HotUpdate子目录下(一般设备上路径/data/data/<appid>/files/HotUpdate/); 将update.apk包中lib/<本测试设备abi>/中的*.so文件解压后直接放置到HotUpdate/update.apk_lib/目录下(可以只放置有修改过的.so文件); 重新运行设备上安装的app,你应该可以看到,已经运行的是新版本!

如何在正式app里自动化热更

app热更应该是一个程序自动化的过程,它将代替我们上面的手工测试过程;(本方案提供了一种可选实现路径)

  • 假设开发了新版本,我可以用diff工具生成新旧版本之间的补丁;将版本升级信息和补丁文件放到服务器上;
  • 客户运行app时检查到升级信息并下载到相应的补丁文件,app用patch算法将本机最新apk和补丁生成新版本apk(即HotUpdate/update.apk), 并选择将apk中有修改过的.so文件缓存到HotUpdate/update.apk_lib/路径;需要“冷”安装时选择不缓存.so文件;
  • 生成apk间的补丁和在设备上合并,使用了ApkDiffPatch项目; diff程序如果不想自己编译,可以在releases下载到;patch过程需要在用户设备上执行,java中提供了virtualApkPatch()函数,C#中可以使用native的virtual_apk_patch()函数;
  • 注意:ApkDiffPatch针对zip进行了特别的优化,一般比bsdiffHDiffPatch生成更小的补丁,其ZipDiff工具对输入的apk文件有特别的要求,如果apk有v2版及以上签名,那需要用库提供的ApkNormalized工具对apk进行标准化,然后再用AndroidSDK#apksigner对apk重新进行签名;所有对用户发布的apk都需要经过这个处理,这是为了patch时能够原样还原apk;(经过这个处理过的apk,也兼容谷歌Play商店的补丁大小优化方案archive-patcher

推荐一个多版本更新流程(较复杂的示例,考虑了支持需要重新安装的情况)

v0 -> new install(v0)

v1 -> diff(v0,v1) -> pat01 ; v0: patch(v0,download(pat01)) -> v1 + cache_lib(v1)
      ( 如果patch时发生错误: download(v1) -> v1 + install(v1) )

v2 -> diff(v0,v2) -> pat02 ; v0: patch(v0,download(pat02)) -> v2 + cache_lib(v2)
      diff(v1,v2) -> pat12 ; v1: patch(v1,download(pat12)) -> v2 + cache_lib(v2)

v3 -> (假设放弃了对v0的补丁) ; v0: download(v3) -> v3 + install(v3)
      diff(v1,v3) -> pat13 ; v1: patch(v1,download(pat13)) -> v3 + cache_lib(v3)
      diff(v2,v3) -> pat23 ; v2: patch(v2,download(pat23)) -> v3 + cache_lib(v3)

if is_need_install(v3,v4):
v4 -> (假设放弃了对v0的补丁) ; v0: download(v4) -> v4 + install(v4)
      diff(v1,v4) -> pat14 ; v1: patch(v1,download(pat14)) -> v4 + install(v4)
      diff(v2,v4) -> pat24 ; v2: patch(v2,download(pat24)) -> v4 + install(v4)
      diff(v3,v4) -> pat34 ; v3: patch(v3,download(pat34)) -> v4 + install(v4)

v5 -> (假设放弃了对v0的补丁) ; v0: download(v5) -> v5 + install(v5)
      (假设放弃了对v1的补丁) ; v1: download(v5) -> v5 + install(v5)
      diff(v2,v5) -> pat25 ; v2: patch(v2,download(pat25)) -> v5 + install(v5)
      diff(v3,v5) -> pat35 ; v3: patch(v3,download(pat35)) -> v5 + install(v5)
      diff(v4,v5) -> pat45 ; v4: patch(v4,download(pat45)) -> v5 + cache_lib(v5)

这个新版本和补丁的发布过程,需要自动化:Apk标准化、重新签名、创建补丁、生成配置等。

已知缺点

  • Java和Unity之间的API变动后不支持热更,新apk需要安装;一般切换升级Unity大版本后无法热更, 小版本切换可以热更;
  • 不能随意切换il2cpp与mono代码打包方式,否则无法热更新,apk需要重新安装;
  • 方案只能支持android,无法应用到iOS上;(PC上Unity开发的app需要支持差异更新可以考虑使用HDiffPatch之类支持目录间diff和patch的方案就可以了)
  • diff&patch方案选择了ApkDiffPatch方案,该方案可能不能支持这种情况:apk必须要支持v2版及以上签名发布,但签名权又不在自己手中,而在渠道手中,并且造成了无法进行版本控制和diff的;
  • 得到的新apk文件和库缓存会长期占用磁盘空间;一个实践方案是:固定Unity版本,初始版本用一个最小化的apk(或者obb分离模式),后续更新后才是完整版本;(另一个可能的改进方案是使用虚拟apk的概念:将没有改变的entry文件利用hook将访问映射到原apk文件里,补丁逻辑也需要另外实现;类似的实现参见UnityAndroidIl2cppPatchDemo)。

by [email protected]

More Repositories

1

HDiffPatch

a C\C++ library and command-line tools for Diff & Patch between binary files or directories(folder); cross-platform; runs fast; create small delta/differential; support large files and limit memory requires when diff & patch.
C++
1,503
star
2

ApkDiffPatch

a C++ library and command-line tools for Zip(Jar,Apk) file Diff & Patch; create minimal delta/differential; support Jar sign(apk v1 sign) & apk v2,v3 sign .
C++
302
star
3

HPatchLite

Lite version of HDiffPatch, tiny code & ram requirements when patch on embedded systems,MCU,NB-IoT,...
C
81
star
4

sfpatcher

stable & fast to patch apk archives, used by Android app store. 为安卓应用商店使用而优化的apk增量更新算法。
72
star
5

FractalBlizzard2

分形风暴2,用来绘制精美的自定义分形图片.
Pascal
50
star
6

demoForHssBlog

demo's source code for my blog: http://blog.csdn.net/housisong
C++
40
star
7

tinyuz

tiny code & ram requirements when decompress on embedded systems,MCU,NB-IoT,...
C++
35
star
8

hsynz

hsynz is a library for delta update using sync algorithm, like zsync. rsync over http(s); implement the sync algorithm on client side, and server side only need http(s) cdn. support compressor zstd & libdeflate & zlib, support large file & directory(folder), support muti-thread.
C++
33
star
9

DGL

Delphi泛型库--DGL(The Delphi Generic Library)
Pascal
27
star
10

libfrg

FRG is an image format(like PNG or JPG or ETC2 or ASTC etc.),for optimize load image time (loading from disk and decoding to display).
C++
25
star
11

lzma

(Unofficial) Git mirror of 7zip & LZMA SDK release v23.01, https://www.7-zip.org . some changes for HDiffPatch.
C++
13
star
12

libmd5

(Unofficial) Git mirror of md5 SDK https://sourceforge.net/projects/libmd5-rfc
C
2
star