是的,没错,前两篇(《如何在Debian中编译Unity Mono生成Android版的libmono.so》、《如何在CentOS/RHEL中编译Unity Mono生成Android版的libmono.so》)中说的需求其实就是Android中Unity的DLL热更新。热更新可以说一直都是游戏(特别是手游)必不可少的功能之一。目前由于苹果对JIT的限制,我们无法在IOS上直接的热更新DLL(最多也只能采取一种曲线救国的方式使用Lua之流的脚本语言调用C# API的方式来实现“热更新”,但也只能某种程度上),但安卓却完全不受此限制。此外,不少的游戏商(特别是中小型寨厂)上线一款游戏都会先上安卓版,然后去各大平台、渠道买量,再根据当前的市场环境以及收到的反馈进行程序上与数值、玩法上的修改和调试,等改得差不多了再上Apple store,上线前期会出现比较多的更新需求。如何热更新Android的DLL?使用反射的方式从外部Load程序集?听起来可行,但这却有着一个致命性的缺点,那就是IOS用不了,这就意味着需要分别对Android和IOS分别进行处理,这只会徒然增加工作量。好在Unity是使用Mono作为运行时并且Mono是开源的,这提供了一种“非官方”的解决方案,那就是改掉Mono的源码,让它可以实现Android的DLL热更。
关于改Mono源码实现Android DLL热更新目前已有不少文章有说明,核心代码基本上都是同一份(包括本文),按照那些代码改基本没有啥太大的问题。本文就仅作一个“炒冷饭”以及额外补充一些我自己遇到的坑以及需要注意的事项。
先说说怎么修改,打开“(unity-mono源码根目录)/mono/metadata/image.c”文件,找到 “mono_image_open_from_data_with_name”函数,把这个函数改成:
MonoImage*mono_image_open_from_data_with_name(char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus*status, gboolean refonly,const char *name)
{/修改开始// intdatasize= 0;if(name!=NULL&&strstr(name,"Assembly-CSharp.dll"))
{//强制要求程序包名都是:com.xxx.yyy
const char *_pack=strstr(name,"com.");const char *_pfie=strstr(name,"-");char_name[512];
memset(_name,0,512);int_len0=(int)(_pfie-_pack);
memcpy(_name,"/data/data/",11);//_name = "/data/data/"
memcpy(_name+ 11, _pack, _len0);//_name = "/data/data/com.xxx.