热更新是什么?
简单的说就是打补丁,只补需要部分,不用重个游戏包重打上传,而是在启动应用程序的时候,在内部进行资源或代码的更新。
手游为什么需要热更新?
手游是快节奏的应用,功能和资源更新频繁,特别是重度手游安装包常常接近1个G,如果不热更新,哪怕改动一行代码也要重新打个包上传到网上让玩家下载。
对于IOS版本的手游包IPA,要上传到苹果商店进行审核,周期漫长,这对于BUG修复类操作是个灾难。
热更新问题的本质是什么?为什么?
本质是代码更新而不是资源更新。
因为手机系统分为Android和IOS,大型手游都是将补丁资源放在专门的WEB服务器上,游戏启动时动态下载并放入到游戏的持久化目录中,这对安卓和苹果都可以
但是代码就特殊了:
由于不同类型的语言有不同的运行机制,编译型语言如C#,是先编译成一整块中间码然后在不同平台上被.NET运行时解释执行,这就是说使用C#编写的APK或IPA安装到手机上后是没有任何C#文件的。这样就算运行时将作为补丁的C#文件从WEB服务器上下载到持久化目录也运行不了。
什么是JIT编译
运行时需要代码时,将 Microsoft中间语言(MSIL) 转换为机器码的编译。
为什么说C#不能热更新
准确的说,C#在安卓上可以实现热更新,但在苹果上却不能。
在安卓上可以通过C#的语言特性-反射机制实现动态代码加载从而实现热更新。
具体做法是:将需要频繁更改的逻辑部分独立出来做成DLL,在主模块调用这些DLL,主模块代码是不修改的,只有作为业务(逻辑)模块的DLL部分需要修改。游戏运行时通过反射机制加载这些DLL就实现了热更新。
但苹果对反射机制有限制,不能实现这样的热更。为什么限制反射机制?安全起见,不能给程序太强的能力,因为反制机制实在太过强大,会给系统带来安全隐患。
c#这种语言编写的程序在运行前,会先编译成中间语言(IL),运行时clr会根据硬件特性,将中间语言解释为机器码,然后申请一块可读写可执行的内存,之前翻译的结果复制过去,指令指针指过去就能运行,这里ios限制了申请的内存权限不可执行
Android支持JIT,但是IOS不允许获取具有可执行权限的内存空间,这就直接要求JIT要以full AOT模式,这种模式把IL直接翻译成机器码而不是在运行期间。因此不能通过直接更新DLL文件更新代码。
为什么手游选择lua热更新方案
主要因为iOS系统的原因。
lua为什么可以热更
lua解释型语言,并不需要事先编译成块,而是运行时动态解释执行的。
lua是使用C写的脚本语言,在运行时读入lua编写的代码,在解释lua时不是翻译为机器码,而是使用C代码进行解释,不用开辟可执行权限的内存空间,也不会有新代码执行,执行解释的是用C语言写出来的lua虚拟机,从始至终没有新机器码生成。
这样lua就和普通的游戏资源如图片,文本没有区别,因此可以在运行时直接从WEB服务器上下载到持久化目录并被其它lua文件调用。
编译型语言和解释型语言的区别
对于计算机硬件而言,它们只能识别某些特定的二进制指令(机器码),而无法解读和直接执行我们编写的源代码。因此,在程序真正运行之前必须将源代码转换成二进制指令。而因为不同语言转换的时机不同,但总体上可分为两类,因而将高级编程语言分为了编译型语言和解释型语言。两者在以下方面存在一定的区别(但不仅限于以下方面):
1、编译型语言在程序在执行之前需要一个专门的编译过程,通过编译器把程序编译成为可执行文件,再由机器运行这个文件,运行时不需要重新翻译,直接使用编译的结果就行了。而解释型语言是一边执行一边转换的,其不会由源代码生成可执行文件,而是先翻译成中间代码,再由解释器对中间代码进行解释运行,每执行一次都要翻译一次。
解释型语言的大致运行流程:
![](https://i-blog.csdnimg.cn/blog_migrate/9480e5ab3407d61c5ad9a83e04423dcf.png)
解释型语言的大致运行流程:
![](https://i-blog.csdnimg.cn/blog_migrate/96eac64d1330ae7ae5d1323e525762ed.png)
2、因为区别(1)的存在,编译型语言可以实现一次编译,无限次运行,只要在首次执行时编译生成相应的可执行文件,在以后的每次运行就只需要直接运行这个可执行文件,因此其运行效率高于解释型语言,但因为不同平台的可执行文件不同(同时不同平台支持的函数、类型、变量等都可能不同),因此编译型语言难以实现在不同操作系统间随意切换,可移植性较差。对于解释型语言,其实现的是一次编写,到处运行,每次执行都得重新转换源代码,因此其在效率上天生就低于编译型语言,但也因为其每次运行都会重新转换源代码,因此只需要解释器根据操作系统的不同将源代码转换成不同的机器码,相同的源代码,就可以实现在不同的平台上运行,因此其更灵活。
编译型语言和解释型语言的大致区别:
![](https://i-blog.csdnimg.cn/blog_migrate/f009a9f20d34077a1f6c34269def7ef4.png)
热更新技术原理
app+脚本解释器+脚本代码,动态执行最新代码,实现热更。
解释器
Lua技术 = Lua解释器+Lua脚本;
C# = C#解释器+c#脚本
Unity的热更方案
Lua解决方案(如ToLua,xLua等):内置Lua虚拟机+UnityEngine与C#框架的接口导出+Lua代码。
C#解决方案(ILRuntime):内置.net字节码解释器(虚拟机)+解释执行.net字节码。
内置虚拟机(lua的虚拟机或者IL虚拟机,均基于栈实现):自己解释执行的一个运行环境,在里面解析执行指令(lua虚拟机的指令/IL语句)
这些热更新方案的VM(虚拟机)与IL2CPP是独立的,意味着它们的元数据系统是不想通的,所以无法直接继承MonoBehaviour(IL2CPP级别的数据对象),导致了以下问题:要封装一层;跨域访问;接口导出;不符合标准的Unity开发。
HybridCLR(huatuo)方案:基于IL2CPP
在Unity的il2cpp里面添加一个可以装载.net字节码,解释执行.net的字节码的功能。
IL2CPP runtime环境(IL2CPP VM)编写一个解释器,解释执行IL代码指令+使用的是AOT的数据内存对象;
IL2CPP 数据内存+代码逻辑指令(二进制机器指令)
IL2CPP_huatuo 数据内存+代码逻辑指令(二进制机器指令)+ IL代码指令解释执行