Unity实现c#热更新方案探究(一)

Unity实现c#热更新方案探究(一)

最近研究了一下如何在unity中实现c#的热更新,对于整个DLL热更新的过程和方案有一个初步的了解,这儿就写下来,便于后续的深入调查和方案选择。

一、C# DLL的动态加载和卸载

既然要热更新,那么就是动态的加载c#的DLL,所以第一步就是研究如何实现DLL的动态加载和卸载。

在CLR Via C#中,对于DLL的加载有详细的讲解,这儿就不再长篇幅的讲解整个过程,简单的来说,在C#的工程中,都会生成一个默认的程序域appDomain,就叫做DefaultAppDomain吧,在这个程序域的基础上,我们可以加载多个不同的程序集。在.Net中,程序集不能卸载,但是可以随着程序域的释放而一起释放,所以我们可以利用程序域来实现程序集(DLL)的加载和释放。

上面的理论来自CLR Via C#, 具体的图为:

基于这个理论,我们可以在DefaultAppDomain之外,再多次创建多个AppDomain,基于AppDomain来实现DLL的加载和卸载。基于此,编写相关的工程测试,参考网上的一个工程来进一步的测试,这儿是原文,文末有相关的代码下载:

程序的热升级

在原代码的基础上,进一步构建。首先,构建4个Class Library:

默认工程为MainServer,将Module1和Module2的Build路径设置到MainServer的bin中,这样MainServer就可以加载最新的Module1.DLL/Module2.DLL(PS:这儿的设置很重要,忽略会使得不能加载最新的DLL)

Module1和Module2都在References中添加CommonLib的引用,实现ICalculater接口,各自的实现为:

Module1:

Module2:

这样,就是两个不同的Class Library中,分别实现了ICalculater接口,分别为相加和相乘。在MainServer的主程序入口Program中:

首先在默认appDomain的基础上,进一步加载2个appDomain,然后分别在这2个程序域的基础上加载DLL。得到的结果为:

整个步骤都详细的解释了整个执行流程,先构建appDomain,在此基础上,加载dll,然后执行里面的方法。再一个新的appDomain中加载前面加载过的dll,再次执行,相互之间并不冲突。所以appDomain可以一对多个DLL,一个DLL可以被多个不同的AppDomain加载。


二、Unity中测试DLL的加载

在第一部分的基础上,我们进一步的研究如何在Unity中实现Dll的加载,基本的操作步骤可以参考这篇文章:unity dll实现热更新

当然,文章并不是完全的实现热更新,实现的是windows和android平台下,对于dll文件的热更新。对于IOS为什么不能热更新,我们后续会讨论到,先看看安卓和windows下 dll的热更新步骤。

1、新建一个ClassLibrary(类库)的工程,在其中实现对应的类和方法;

2、将该工程导出为DLL;

3、将DLL改为bytes文件,存入Unity工程中的StreamingAssets文件夹下;

4、在工程运行的时候,读取StreamingAssets下的Dll文件,用Assembly.Load(byte[] bytes )的方法,将DLL文件读取出来,进而执行相关的操作。这一步的代码为:

对于DLL文件,是执行www.bytes,对于assetbundle文件,则是执行ab.mainAsset转换为TextAsset,进一步得到bytes。在windows和android平台下,都会得到这样的屏幕输出:

这个方案的本质,和前面的本质相差不大,unity工程在执行的时候,会构建一个默认的appDomain,Assembly.Load,其实就是在这个程序域上加载Dll,所以相关的实质和前面一个部分相差不大,这就是c#热更新在unity中的应用(IOS不包括)。

下文我们会讲解IOS为什么不支持DLL的热更新,以及如何利用ILRuntime来实现Android和IOS的热更新。

PS: 最近进行来多种测试,有以下的消息更新

1、如果将热更新的DLL放在非StreamingAssets目录下,那么在unity工程启动的时候,会被默认加载到当前程序域,那么此时再Assembly.Load热更新的DLL,是不会覆盖前面的DLL的,这时候执行的是前面默认加载进来的DLL中的信息,热更新失效;

2、由于默认加载DLL的存在,所以不能在场景/UI中挂载热更新DLL中的继承自UnityEngine.Component以及其子类(比如MonoBehaviour)的组件,除非这个类不被热更,那其实没必要放在热更工程中了。

编辑于 2018-05-29

文章被以下专栏收录

    在日常的开发当中总是会发现一些问题,发现一些让我们开发不那么愉快的地方。我们需要去不断更新我们的知识与工具去克服这些困难,带来更高的开发效率! 这就是我为什么不断寻找更好的开发技巧的理由! 没有最好,只有更好! 当然鄙人才疏学浅,如果有思考不周全的地方请一定要提出建议和意见!