自定义Mono,实现Unity Android平台代码更新

发表于2017-11-18
评论0 4.2k浏览

原理

使用自定义Mono,实现Unity Android平台代码更新之前,需要了解:

1、Unity是基于Mono的,我们写的代码都被编译成DLL,然后由Mono读取这个DLL并解析运行。

2、在Mono眼里,DLL和普通的资源文件没什么区别。


现在项目有代码热更的需求,而去年的虚拟机被删除了。就重新走了一遍流程。


转自http://blog.csdn.net/huutu http://www.liveslives.com http://www.thisisgame.com.cn

在 Android 中,由 libmono.so 来加载 Assembly-CSharp.dll 。

libmono.so  这就是 Mono 了 。



Unity3d 是基于 Mono2.0 的,而 Mono2.0是免费开源的。所以基于各种开源协议 ,Unity 官方也将自己修改过的 Mono 开源出来,我们下载过来然后修改 重新编译出自己的 libmono.so 。

项目托管在 Github 上,项目地址:https://github.com/Unity-Technologies/mono  


我们项目用的是Unity5.3.2,所以下载mono的分支:https://github.com/Unity-Technologies/mono/tree/unity-5.3  

了解到一些原理背景后就可以开始进行操作了。

1、安装ubuntu系统 


Ubuntu 系统下载:http://www.ubuntu.org.cn/download/desktop  

http://old-releases.ubuntu.com/releases/14.04.1/ubuntu-14.04.1-desktop-i386.iso?_ga=1.187436840.1241524278.1457318071    


如果你对Ubuntu不是很熟悉的话,我建议重新创建一个新的虚拟机。

我这里安装的是32位的Ubuntu14.


安装好Ubuntu之后,首先要设置一下软件源为国内的镜像站,这样安装软件会很快。

在左边的快速启动面板中点击“软件中心”按钮,

再把鼠标移到最上边出现菜单栏,点“编辑-软件源”

转自http://blog.csdn.net/huutu http://www.liveslives.com http://www.thisisgame.com.cn

在出来的软件源面板下边,点击“下拉列表,选择“其他站点”;


在出来的服务器列表中,选择搜狐、163 或 cn99 的站点都可以,然后点右下角的“选择服务器”按钮返回;



2、下载ANDROID_NDK

安装完 Ubuntu 后,在 Ubuntu 中   ,注意32 和64位区别

64 位下载 :http://pan.baidu.com/s/1dDAqnK1


32位下载  :http://pan.baidu.com/s/1sjoneRr   

sudo su 切换到root安装

./android-ndk-r10e-linux-x86.bin  

安装后在安装目录里面找到 RELEASE.txt ,里面记录着NDK 完整版本号,修改为 r10e

(Mono的编译脚本是读取这个RELEASE.txt中记录的版本号,然后和编译脚本中填写的版本号做匹配的,如果不匹配就会去Google下载)


设置环境变量 ANDROID_NDK_ROOT
sudo gedit /etc/bashrc  

添加一行

export ANDROID_NDK_ROOT=/home/captain/Downloads/android-ndk-r10e;  


让环境变量立即生效

source /etc/bashrc

测试是否添加成功

echo $ANDROID_NDK_ROOT 


3、编译 Development 版本的 libmono.so

1、从 Github 下载 unity-mono,我这里下载的5.3版本:https://github.com/Unity-Technologies/mono/tree/unity-5.3  


如果你也是Unity5.3版本,那么可以直接Clone我编译好的Mono:https://github.com/ThisisGame/Unity5.3_Android_DLL_HotFix  


2、下载好之后解压,然后拷贝编译脚本到mono根目录

cp external/buildscripts/build_runtime_android.sh ./  

3、运行编译脚本,提示没有安装git,安装git

sudo apt-get install autoconf  

4、修改脚本build_runtime_android.sh
sudo gedit build_runtime_android.sh  

搜索KRAIT_PATCH_PATH 修改

KRAIT_PATCH_PATH="${CWD}/android_krait_signal_handler/build"  

搜索(cd "$KRAIT_PATCH_PATH" &&修改

(cd "$KRAIT_PATCH_PATH" && perl ./build.pl)  

5、android_krait_signal_handler/build.pl 删除第一行注释

转自http://blog.csdn.net/huutu http://www.liveslives.com http://www.thisisgame.com.cn

6、安装其它的依赖库

* autoconf    
* automake    
* bison    
* gcc    
* gettext    
* glib >= 2.0    
* libtool    
* make    
* perl  

7、再次执行脚本,编译成功。


这个so 就是mono的库,我们可以用它替换掉unity自带的。


4、修改mono源码,读取下载的新版本DLL

找到 /metadata/image.c 这个文件

找到
mono_image_open_from_data_with_name  

修改如下:
  1. static FILE* OpenFileWithPath( const char* path )  
  2.   
  3. {  
  4.   
  5.     const char* fileMode ="rb";  
  6.   
  7.     return fopen(path, fileMode );  
  8.   
  9. }  
  10.   
  11.   
  12. static char* ReadStringFromFile(const char* pathName,int* size)  
  13.   
  14. {  
  15.   
  16.     FILE* file = OpenFileWithPath( pathName);  
  17.   
  18.     if (file == NULL)  
  19.   
  20.         return 0;  
  21.   
  22.       
  23.   
  24.     fseek(file, 0, SEEK_END);  
  25.   
  26.       
  27.   
  28.     int length = ftell(file);  
  29.   
  30.     fseek(file, 0, SEEK_SET);  
  31.   
  32.     if (length < 0)  
  33.   
  34.     {  
  35.   
  36.         fclose( file );  
  37.   
  38.         return 0;  
  39.   
  40.     }  
  41.   
  42.     *size = length;  
  43.   
  44.     char* outData = g_try_malloc (length);  
  45.   
  46.     int readLength = fread(outData, 1, length, file);  
  47.   
  48.       
  49.   
  50.     fclose(file);  
  51.   
  52.       
  53.   
  54.     if (readLength != length)  
  55.   
  56.     {  
  57.   
  58.         //if(readLength == length){  
  59.   
  60.         //    JNI_OnLoad(0,0);  
  61.   
  62.         //    JNI_OnUnload(0,0);  
  63.   
  64.         //}  
  65.   
  66.         g_free (outData);  
  67.   
  68.         return 0;  
  69.   
  70.     }  
  71.   
  72.       
  73.   
  74.     return outData;  
  75.   
  76. }  
  77.   
  78. MonoImage *  
  79. mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)  
  80. {  
  81.     //修改开始1  
  82.     int datasize=0;  
  83.     if(strstr(name,"Assembly-CSharp.dll"))  
  84.     {  
  85.         //重新计算路径  
  86.         const char* _pack = strstr(name,"com.");  
  87.         const char* _pfie = strstr(name,"-");  
  88.         char _name[512];  
  89.         memset(_name,0,512);  
  90.         int _len0 = (int)(_pfie - _pack);  
  91.         memcpy(_name , "/data/data/",11);  
  92.         memcpy(_name + 11, _pack,_len0);  
  93.         memcpy(_name + 11 + _len0 ,"/files/Assembly-CSharp.dll",26);  
  94.         g_message("momo: path = %s\n", _name);  
  95.         char* bytes = ReadStringFromFile (_name,&datasize);  
  96.         if(datasize > 0)  
  97.         {  
  98.             data = bytes;  
  99.             data_len = datasize;  
  100.         }  
  101.     }  
  102.     if(strstr(name,"Assembly-CSharp-firstpass.dll"))  
  103.     {  
  104.         //重新计算路径  
  105.   
  106.                 const char* _pack = strstr(name,"com.");  
  107.   
  108.             const char* _pfie = strstr(name,"-");  
  109.   
  110.             char _name[512];  
  111.   
  112.             memset(_name,0,512);  
  113.   
  114.             int _len0 = (int)(_pfie - _pack);  
  115.   
  116.             memcpy(_name , "/data/data/",11);  
  117.   
  118.             memcpy(_name + 11, _pack,_len0);  
  119.   
  120.             memcpy(_name + 11 + _len0 ,"/files/Assembly-CSharp-firstpass.dll",36);  
  121.   
  122.           
  123.   
  124.         g_message("momo: path = %s\n", _name);  
  125.   
  126.           
  127.   
  128.         char* bytes = ReadStringFromFile (_name,&datasize);  
  129.   
  130.         if(datasize > 0)  
  131.         {  
  132.   
  133.             data = bytes;  
  134.   
  135.             data_len = datasize;  
  136.   
  137.         }  
  138.     }  
  139.     //修改结束1  
  140.       
  141.     MonoCLIImageInfo *iinfo;  
  142.     MonoImage *image;  
  143.     char *datac;  
  144.   
  145.     if (!data || !data_len) {  
  146.         if (status)  
  147.             *status = MONO_IMAGE_IMAGE_INVALID;  
  148.         return NULL;  
  149.     }  
  150.     datac = data;  
  151.     if (need_copy) {  
  152.         datac = g_try_malloc (data_len);  
  153.         if (!datac) {  
  154.             if (status)  
  155.                 *status = MONO_IMAGE_ERROR_ERRNO;  
  156.             return NULL;  
  157.         }  
  158.         memcpy (datac, data, data_len);  
  159.     }  
  160.   
  161.     //修改开始2  
  162.   
  163.     if(datasize > 0 && data != 0)  
  164.     {  
  165.   
  166.         g_free (data);   
  167.   
  168.     }  
  169.     //修改结束2  
  170.   
  171.     image = g_new0 (MonoImage, 1);  
  172.     image->raw_data = datac;  
  173.     image->raw_data_len = data_len;  
  174.     image->raw_data_allocated = need_copy;  
  175.     image->name = (name == NULL) ? g_strdup_printf ("data-%p", datac) : g_strdup(name);  
  176.     iinfo = g_new0 (MonoCLIImageInfo, 1);  
  177.     image->image_info = iinfo;  
  178.     image->ref_only = refonly;  
  179.     image->ref_count = 1;  
  180.   
  181.     image = do_mono_image_load (image, status, TRUE, TRUE);  
  182.     if (image == NULL)  
  183.         return NULL;  
  184.   
  185.     return register_image (image);  
  186. }  

修改的效果是,mono在读取完默认位置的dll之后,我们添加的代码会判断有没有下载的新版本的dll位于 /data/data/xx/file 目录,如果有,就读取新版本的dll替换掉默认位置的dll。


保存后,重新编译so文件。


5、编译Release版本

修改所有的 build_runtime_android.sh

找到 -fpic -g 去掉 -g

这样再编译就得到Release版本的so文件

转自http://blog.csdn.net/huutu http://www.liveslives.com http://www.thisisgame.com.cn

6、导出工程,可以用脚本自动替换so文件。

  1. /**  
  2.  * 文件名:BuildPostprocessor.cs  
  3.  * Des:在导出Eclipse工程之后对替换mono.so 
  4.  * Author:Captain  
  5.  * **/  
  6.   
  7.   
  8. using UnityEngine;  
  9. using UnityEditor;  
  10. using UnityEditor.Callbacks;  
  11. using System.IO;  
  12.   
  13. public class BuildPostprocessor  
  14. {  
  15.     [PostProcessBuildAttribute(1)]  
  16.     public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)  
  17.     {  
  18.         if (target == BuildTarget.Android && (!pathToBuiltProject.EndsWith(".apk")))  
  19.         {  
  20.             Debug.Log("target: " + target.ToString());  
  21.             Debug.Log("pathToBuiltProject: " + pathToBuiltProject);  
  22.             Debug.Log("productName: " + PlayerSettings.productName);  
  23.   
  24.             Debug.Log("Current is : " + EditorUserBuildSettings.development.ToString());  
  25.   
  26.             //替换 libmono.so;    
  27.             if (EditorUserBuildSettings.development)  
  28.             {  
  29.                 string armv7a_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/armeabi-v7a/libmono.so";  
  30.                 File.Copy(Application.dataPath + "/HotFix/Editor/libs/development/armeabi-v7a/libmono.so", armv7a_so_path, true);  
  31.   
  32.                 string x86_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/x86/libmono.so";  
  33.                 File.Copy(Application.dataPath + "/HotFix/Editor/libs/development/x86/libmono.so", x86_so_path, true);  
  34.             }  
  35.             else  
  36.             {  
  37.                 string armv7a_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/armeabi-v7a/libmono.so";  
  38.                 File.Copy(Application.dataPath + "/HotFix/Editor/libs/release/armeabi-v7a/libmono.so", armv7a_so_path, true);  
  39.   
  40.                 string x86_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/x86/libmono.so";  
  41.                 File.Copy(Application.dataPath + "/HotFix/Editor/libs/release/x86/libmono.so", x86_so_path, true);  
  42.             }  
  43.   
  44.             Debug.Log("HotFix libmono.so Success !!");  
  45.         }  
  46.     }  
  47. }  


如果你也是Unity5.3版本,那么可以直接Clone我编译好的Mono:https://github.com/ThisisGame/Unity5.3_Android_DLL_HotFix  

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引