0%

热更新

内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据,常用于页面置换算法,是为虚拟页式存储管理服务的。

一、基础

  1. 关于appstore禁用热更新的通知
1
2
3
4
5
6
7
8
9
10
11
Dear Developer,In March of this year we notified you that your app contains code 
designed explicitly with the capability to change your app’s behavior or
functionality after App Store Review approval, which is not in compliance with
section 3.3.2 of the Apple Developer Program License Agreement and App Store
Review Guideline 2.5.2. We requested that you remove any code, frameworks,
or SDKs that fall in line with the functionality described above before
submitting the next update for your app for review.
As of this message, we have not received a compliant update for your app.
To ensure there is no interruption of the availability of your app,
please submit an update by June 12th, 2017. If we do not receive an update
by that date, your app may be removed from the App Store.

      翻译过来也没太大意思,根据大部分开发者的反应以及官方的说法,appstore此次禁用的主要是几种热更新框架和技术,如dlopen、dlsym、respondingToSelector、 performSelector、method_exchangeImplementationsd、JSPatch等,做过app开发的同学应该都知道,通过远程下载的脚本使用这些函数进而改变app行为,显然与appstore的封闭、安全等理念相违背(相信这也是大部分果粉选择ios的原因)。

JSPatch的原理是开发者编写JavaScript代码,通过使用苹果内置的JavaScriptCore.Framework来执行以实现热更新功能,通俗的理解就是JSPatch为所有的Objective-CAPI进行了映射,允许开发者在JS端调用任意原生代码,显然这是危险的。

  1. 热更新技术

      热更新简而言之就是在服务器不关闭的情况下,用户打开应用即可下载更新代码运行,这是目前移动游戏更新的主流方式之一。

那么为什么苹果没有禁用游戏的热更新呢?

      游戏热更新主要的实现方式是把动态脚本(如Python、Lua等运行时动态解释执行)下载之后,让动态脚本调用游戏引擎提供的接口实现bug修复、UI/策略/玩法更新等。与JSPatch不同的是,动态脚本并不能任意调用全部原生代码,而是只能根据游戏引擎提供的接口调用相关功能。在这个过程中,游戏引擎的原生端作为一个安全沙箱,提供了一个安全的保护层,只要游戏引擎不要对外提供获取通讯录等接口,黑客就无法通过替换动态脚本的方式获取用户相关隐私资料,进而可以被认为是安全的,自然不在苹果的禁止范围内。

  1. app的组成,从方便理解的角度去看,可以将整个app拆成两个部分:
    • 基础部分:底层的、不经常变动的部分。
    • 业务部分:基于基础部分之上变动频繁的部分。

通常基础部分是无法热更新的,如果要修改这部分就得重新打包制作安装包(如安卓的apk/ios的ipa)给应用商店。而业务部分则是可以被热更新的,而通常负责业务部分热更新功能的正是基础部分。对于很多游戏来说,通常会把javascript、lua、python等这些脚本语言代码放在业务部分,这样一来游戏中的绝大部分业务内容就都可以实时更新了。

APK,即Android应用程序包(Android application package),它是Android操作系统使用的一种应用程序包文件格式,用于分发和安装移动应用及中间件。一个Android应用程序的代码想要在Android设备上运行,必须先进行编译,然后被打包成为一个被Android系统所能识别的文件才可以被运行,而这种能被Android系统识别并运行的文件格式便是APK。 一个APK文件内包含被编译的代码文件(.dex 文件),文件资源(resources), assets,证书(certificates),和清单文件(manifest file)。

IPA后缀的文件是iOS系统的软件包,全称为iPhone application archive。通常情况下,IPA文件都是使用苹果公司的FairPlayDRM技术进行加密保护的。每个IPA文件都是ARM架构的可执行文件以及该应用的资源文件的打包文件,只能安装在iPhone,iPod Touch或iPad上。该文件可以通过修改后缀名为zip后,进行解压缩,查看其软件包中的内容。

二、一次热更新过程

  1. 快速开发一个游戏app并编译成一个安装包(如apk)上架,我们把这种完整的安装包称为母包。为了方便描述,我们把这个母包称为母包1,这时候的客户端版本,记作1.0
1
2
3
4
5
6
7
8
9
## 基础部分
a.cs
b.cs
c.cs

## 可热更部分
1.mp4
2.lua
3.txt

比方说我们的母包1包含了这么些东西,然后我们把这个1.0客户端给放在了应用商店,提供给用户下载使用。

  1. 游戏app的特点就是快,热门的游戏一般每周都会发版本(其中可能有bug修复、玩法升级等)。比如我们这次新加了玩法以及新手引导(修改1.mp4,增加4.lua),客户端版本记为1.1,包含的内容如下:
1
2
3
4
5
6
7
8
9
10
## 基础部分
a.cs
b.cs
c.cs

## 可热更部分
1.mp4 # 修改
2.lua
3.txt
4.lua

      我们在1.0版本的基础上,修改了一个文件又增加了一个文件。这时候我们希望玩家的游戏都同步到这个新版本来,改该怎么办呢?最传统的方法就是我再编译一个安装包,扔给应用商店。但是这样做就有问题了,比如说一个安装包有10G,但是就修改了10kb的内容,每次更新完后都让玩家下载10个G的安装包,这显然是不合理的。这时候,就该补丁包出场了。

      我们制作了一个补丁包,它包含此次修改的文件和增加的文件,然后放在我们的服务器上。玩家进入游戏后的会自动下载这个补丁文件(具体实现不同的游戏有不同的实现方式,大体就是检查当前客户端的版本号和服务器上的版本号,通过对比差异来进行更新),下载完成后把这两个改动的文件更新到原来的客户端中(脚本语言解释执行),这样一来玩家手里的客户端就是今天的最新版本了。

1
2
3
## 补丁1
1.mp4
4.lua
  1. 下周版本又修改了一些内容(增加5.lua),版本记为1.2,内容如下:
1
2
3
4
5
6
7
8
9
10
11
## 基础部分
a.cs
b.cs
c.cs

## 可热更部分
1.mp4
2.lua
3.txt
4.lua
5.lua

现在想把这个最新版本同步更新给玩家,需要再打个补丁包叫补丁2,此时有两种方式

  • 方式1:把相对于1.0版本的所有变动内容都放在补丁2中,即版本1.2 = 母包1.0 + 始终最新版本的一个补丁,补丁2内容如下:

    1
    2
    3
    1.mp4
    4.lua
    5.lua
  • 方式2:在补丁1(版本1.1)的基础上,制作补丁2(版本1.2),即版本1.2 = 母包1.0 + 补丁1 + 补丁2(依次),补丁2内容如下:

    1
    5.lua
  1. 继上个版本1.2之后,此次没有修改任何东西但重新编译了一个安装包,记作母包2,版本记为2.0,包含内容如下:
1
2
3
4
5
6
7
8
9
10
11
## 基础部分
a.cs
b.cs
c.cs

## 可热更部分
1.mp4
2.lua
3.txt
4.lua
5.lua

母包2中的内容和母包1加两补丁的1.2版本的内容是一模一样的。这时候把母包2放在应用商店中给新来的用户下载,那么下载了母包2的用户打开就直接能玩了,不需要再下载补丁来更新客户端。与此同时,之前下载了我们母包1并且通过补丁更新到1.2版本的玩家,也不需要重新去下载一个母包2就可以获得一致的游戏内容。

三、参考

  1. 参考一
  2. 参考二
  3. 参考三
  4. 参考四