3.Unity调用ILRuntime-Unity中启动ILRuntime
3.1 知识点
ILRuntime关键类AppDomain
ILRuntime的开发方式是在Unity主工程和ILRuntime热更工程中进行开发的。两个工程之间可以相互访问调用。
ILRuntime热更代码最终会生成一个dll文件和一个pdb文件,这里面就包含了我们热更代码的相关信息。
ILRuntime提供了AppDomain类,是ILRuntime提供的用于解释执行dll和pdb文件的。通过它我们才能解释执行我们的热更代码。它的作用就有点类似xLua中的LuaEnv lua解析器。
Unity中启动ILRuntime
private AppDomain appDomain;
//用于存储加载出来的 两个文件的内存流对象
private MemoryStream dllMemoryStream;
private MemoryStream pdbStream;
void Start()
{
//启动方式
//声明AppDomain对象(命名空间 ILRuntime.Runtime.Enviorment)
appDomain = new AppDomain();
//加载本地或远端下载的dll和pdb文件
StartCoroutine(LoadHotUpdateInfo());
//将加载的数据以流的形式(文件流或者内存流对象)传递给AppDomain对象中的LoadAssembly方法
//初始化ILRuntime相关信息(目前只需要告诉ILRuntime主线程的线程ID,主要目的是能够在Unity的Profiler剖析器窗口中分析问题)
//执行热更代码中的逻辑
//注意销毁内存流时机
//注意:一般在一个项目中,大多数情况下只需要一个AppDomain对象
}
/// <summary>
/// 去异步加载我们的热更相关的dll和pdb文件
/// </summary>
/// <returns></returns>
IEnumerator LoadHotUpdateInfo()
{
//加载本地或远端下载的dll和pdb文件
//加载本地的DLL文件
#if UNITY_ANDROID
UnityWebRequest dllUnityWebRequest = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.dll");
#else
UnityWebRequest dllUnityWebRequest = UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll");
#endif
yield return dllUnityWebRequest.SendWebRequest();
//如果失败了
if (dllUnityWebRequest.result != UnityWebRequest.Result.Success)
{
print("加载DLL文件失败" + dllUnityWebRequest.responseCode + dllUnityWebRequest.result);
}
//读取加载的DLL数据
byte[] dllByteArray = dllUnityWebRequest.downloadHandler.data;
dllUnityWebRequest.Dispose();
//加载本地的pdb文件
#if UNITY_ANDROID
UnityWebRequest pdbUnityWebRequest = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.pdb");
#else
UnityWebRequest pdbUnityWebRequest = UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/HotFix_Project.pdb");
#endif
yield return pdbUnityWebRequest.SendWebRequest();
//如果失败了
if (pdbUnityWebRequest.result != UnityWebRequest.Result.Success)
{
print("加载Pdb文件失败" + pdbUnityWebRequest.responseCode + pdbUnityWebRequest.result);
}
//读取加载的DLL数据
byte[] pdbByteArray = pdbUnityWebRequest.downloadHandler.data;
pdbUnityWebRequest.Dispose();
//将加载的数据以流的形式(文件流或者内存流对象)传递给AppDomain对象中的LoadAssembly方法
//注意 这里使用的内存流 不要用完就关 一定要等到热更相关内容不使用了 再关闭 因为热更代码会不停访问这两个流
dllMemoryStream = new MemoryStream(dllByteArray);
pdbStream = new MemoryStream(pdbByteArray);
//将我们两个文件的内存流用于初始化 appDomain 我们之后就可以通过该对象来执行我们对应的热更代码了
appDomain.LoadAssembly(dllMemoryStream, pdbStream, new PdbReaderProvider());
//初始化ILRuntime相关信息(目前只需要告诉ILRuntime主线程的线程ID,主要目的是能够在Unity的Profiler剖析器窗口中分析问题)
appDomain.UnityMainThreadID = Thread.CurrentThread.ManagedThreadId;
//执行热更代码中的逻辑
print("我们已经对ILRuntime初始化成功 之后就可以执行对应逻辑了");
}
//注意销毁内存流时机
private void OnDestroy()
{
//释放流
if (dllMemoryStream != null)
dllMemoryStream.Close();
if (pdbStream != null)
pdbStream.Close();
dllMemoryStream = null;
pdbStream = null;
//清空appDomain
appDomain = null;
}
总结
从本节Unity中启动ILRuntime我们更能够感受到,ILRuntime热更新的内容其实就是热更工程中的dll文件和pdb文件,这两个文件中就包含了我们所有的热更C#代码信息。以后如果我们要做远端热更新,只需要把他们放入AB包下载即可。
3.2 知识点代码
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using ILRuntime.Mono.Cecil.Pdb;
using ILRuntime.Runtime.Enviorment;
using UnityEngine;
using UnityEngine.Networking;
public class Lesson03_Unity调用ILRuntime_Unity中启动ILRuntime : MonoBehaviour
{
#region 知识点二 Unity中启动ILRuntime
private AppDomain appDomain;
//用于存储加载出来的 两个文件的内存流对象
private MemoryStream dllMemoryStream;
private MemoryStream pdbStream;
#endregion
void Start()
{
#region 知识点一 ILRuntime关键类AppDomain
//上节课,我们知道ILRuntime的开发方式
//是在Unity主工程和ILRuntime热更工程中进行开发的
//两个工程之间可以相互访问调用
//ILRuntime热更代码最终会生成一个dll文件和一个pdb文件
//这里面就包含了我们热更代码的相关信息
//而ILRuntime提供了AppDomain类
//是ILRuntime提供的用于解释执行dll和pdb文件的
//通过它我们才能解释执行我们的热更代码
//它的作用就有点类似xLua中的LuaEnv lua解析器
#endregion
#region 知识点二 Unity中启动ILRuntime
//启动方式
//1.声明AppDomain对象(命名空间 ILRuntime.Runtime.Enviorment)
appDomain = new AppDomain();
//2.加载本地或远端下载的dll和pdb文件
StartCoroutine(LoadHotUpdateInfo());
//3.将加载的数据以流的形式(文件流或者内存流对象)传递给AppDomain对象中的LoadAssembly方法
//4.初始化ILRuntime相关信息(目前只需要告诉ILRuntime主线程的线程ID,主要目的是能够在Unity的Profiler剖析器窗口中分析问题)
//5.执行热更代码中的逻辑
//6.注意销毁内存流时机
//注意:
//一般在一个项目中,大多数情况下只需要一个AppDomain对象
#endregion
#region 总结
//从本节Unity中启动ILRuntime我们更能够感受到
//ILRuntime热更新的内容其实就是热更工程中的dll文件和pdb文件
//这两个文件中就包含了我们所有的热更C#代码信息
//以后如果我们要做远端热更新,只需要把他们放入AB包下载即可
#endregion
}
#region 知识点二 Unity中启动ILRuntime
/// <summary>
/// 去异步加载我们的热更相关的dll和pdb文件
/// </summary>
/// <returns></returns>
IEnumerator LoadHotUpdateInfo()
{
//2.加载本地或远端下载的dll和pdb文件
//使用UnityWebRequest 这部分知识点在 Unity网络开发基础当中有讲解
//加载本地的DLL文件
#if UNITY_ANDROID
UnityWebRequest dllUnityWebRequest = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.dll");
#else
UnityWebRequest dllUnityWebRequest = UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll");
#endif
yield return dllUnityWebRequest.SendWebRequest();
//如果失败了
if (dllUnityWebRequest.result != UnityWebRequest.Result.Success)
{
print("加载DLL文件失败" + dllUnityWebRequest.responseCode + dllUnityWebRequest.result);
}
//读取加载的DLL数据
byte[] dllByteArray = dllUnityWebRequest.downloadHandler.data;
dllUnityWebRequest.Dispose();
//加载本地的pdb文件
#if UNITY_ANDROID
UnityWebRequest pdbUnityWebRequest = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.pdb");
#else
UnityWebRequest pdbUnityWebRequest = UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/HotFix_Project.pdb");
#endif
yield return pdbUnityWebRequest.SendWebRequest();
//如果失败了
if (pdbUnityWebRequest.result != UnityWebRequest.Result.Success)
{
print("加载Pdb文件失败" + pdbUnityWebRequest.responseCode + pdbUnityWebRequest.result);
}
//读取加载的DLL数据
byte[] pdbByteArray = pdbUnityWebRequest.downloadHandler.data;
pdbUnityWebRequest.Dispose();
//3.将加载的数据以流的形式(文件流或者内存流对象)传递给AppDomain对象中的LoadAssembly方法
//注意 这里使用的内存流 不要用完就关 一定要等到热更相关内容不使用了 再关闭 因为热更代码会不停访问这两个流
dllMemoryStream = new MemoryStream(dllByteArray);
pdbStream = new MemoryStream(pdbByteArray);
//将我们两个文件的内存流用于初始化 appDomain 我们之后就可以通过该对象来执行我们对应的热更代码了
appDomain.LoadAssembly(dllMemoryStream, pdbStream, new PdbReaderProvider());
//4.初始化ILRuntime相关信息(目前只需要告诉ILRuntime主线程的线程ID,主要目的是能够在Unity的Profiler剖析器窗口中分析问题)
appDomain.UnityMainThreadID = Thread.CurrentThread.ManagedThreadId;
//5.执行热更代码中的逻辑
print("我们已经对ILRuntime初始化成功 之后就可以执行对应逻辑了");
}
//6.注意销毁内存流时机
private void OnDestroy()
{
//6.1 释放流
if (dllMemoryStream != null)
dllMemoryStream.Close();
if (pdbStream != null)
pdbStream.Close();
dllMemoryStream = null;
pdbStream = null;
//6.2 清空appDomain
appDomain = null;
}
#endregion
}
3.3 练习题
请基于本节课,做一个ILRuntime的单例模式管理器,专门用于管理ILRuntime的启动
创建ILRuntimeManager,实现单例
public class ILRuntimeManager : BaseSingletonInMonoBehaviour
{
}
定义appDomain,流对象。是否启动标识
public class ILRuntimeManager : BaseSingletonInMonoBehaviour
{
public AppDomain appDomain;
private MemoryStream dllStream;
private MemoryStream pdbStream;
private bool isStart = false;
}
定义启动ILRuntime方法,开启协程,外部可以传入开启成功回调
public void StartILRuntime(UnityAction callBack = null)
{
if (!isStart)
{
isStart = true;
appDomain = new AppDomain();
StartCoroutine(LoadHotUpdateInfo(callBack));
}
}
协程主要做的事是加载dll和pdb到流对象,帮助初始化appDomain
private IEnumerator LoadHotUpdateInfo(UnityAction callBack)
{
// 加载DLL文件
UnityWebRequest reqDll = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.dll");
yield return reqDll.SendWebRequest();
if (reqDll.result != UnityWebRequest.Result.Success)
print("加载DLL文件失败" + reqDll.responseCode + reqDll.result);
byte[] dll = reqDll.downloadHandler.data;
reqDll.Dispose();
// 加载Pdb文件
UnityWebRequest reqPdb = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.pdb");
yield return reqPdb.SendWebRequest();
if (reqPdb.result != UnityWebRequest.Result.Success)
print("加载Pdb文件失败" + reqPdb.responseCode + reqPdb.result);
byte[] pdb = reqPdb.downloadHandler.data;
reqPdb.Dispose();
// 将加载的数据以流的形式传递给AppDomain对象中的LoadAssembly方法
dllStream = new MemoryStream(dll);
pdbStream = new MemoryStream(pdb);
appDomain.LoadAssembly(dllStream, pdbStream, new PdbReaderProvider());
// 其他初始化
InitILRuntime();
ILRuntimeLoadOverDoSomthing();
// 回调
callBack?.Invoke();
}
定义初始化方法和初始化完成后要热更新的逻辑方法
private void InitILRuntime()
{
// 初始化ILRuntime相关信息
appDomain.UnityMainThreadID = Thread.CurrentThread.ManagedThreadId;
}
private void ILRuntimeLoadOverDoSomthing()
{
// 启动完毕并且初始化完毕后要执行的热更新逻辑
}
进行测试
ILRuntimeManager.Instance.StartILRuntime((() => { print("启动ILRuntime成功"); }));
3.4 练习题代码
ILRuntimeManager
using ILRuntime.Mono.Cecil.Pdb;
using ILRuntime.Runtime.Enviorment;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
namespace BaseFramework
{
public class ILRuntimeManager : BaseSingletonInMonoBehaviour<ILRuntimeManager>
{
public AppDomain appDomain;
//用于存储加载出来的 两个文件的内存流对象
private MemoryStream dllStream;
private MemoryStream pdbStream;
private bool isStart = false;
/// <summary>
/// 用于启动ILRuntime初始化方法
/// </summary>
public void StartILRuntime(UnityAction callBack = null)
{
if (!isStart)
{
isStart = true;
appDomain = new AppDomain();
StartCoroutine(LoadHotUpdateInfo(callBack));
}
}
public void StopILRuntime()
{
//1.释放流
if (dllStream != null)
dllStream.Close();
if (pdbStream != null)
pdbStream.Close();
dllStream = null;
pdbStream = null;
//2.清空appDomain
appDomain = null;
isStart = false;
}
/// <summary>
/// 初始化ILRuntime相关的内容
/// </summary>
private void InitILRuntime()
{
//其他初始化
//初始化ILRuntime相关信息(目前只需要告诉ILRuntime主线程的线程ID,主要目的是能够在Unity的Profiler剖析器窗口中分析问题)
appDomain.UnityMainThreadID = Thread.CurrentThread.ManagedThreadId;
}
/// <summary>
/// 启动完毕并且初始化完毕后 想要执行的热更新的逻辑
/// </summary>
private void ILRuntimeLoadOverDoSomthing()
{
}
/// <summary>
/// 去异步加载我们的热更相关的dll和pdb文件
/// </summary>
/// <returns></returns>
IEnumerator LoadHotUpdateInfo(UnityAction callBack)
{
//这部分知识点在 Unity网络开发基础当中有讲解
//加载本地的DLL文件
#if UNITY_ANDROID
UnityWebRequest reqDll = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.dll");
#else
UnityWebRequest reqDll =
UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll");
#endif
yield return reqDll.SendWebRequest();
//如果失败了
if (reqDll.result != UnityWebRequest.Result.Success)
print("加载DLL文件失败" + reqDll.responseCode + reqDll.result);
//读取加载的DLL数据
byte[] dll = reqDll.downloadHandler.data;
reqDll.Dispose();
#if UNITY_ANDROID
UnityWebRequest reqPdb = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.pdb");
#else
UnityWebRequest reqPdb =
UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/HotFix_Project.pdb");
#endif
yield return reqPdb.SendWebRequest();
//如果失败了
if (reqPdb.result != UnityWebRequest.Result.Success)
print("加载Pdb文件失败" + reqPdb.responseCode + reqPdb.result);
//读取加载的DLL数据
byte[] pdb = reqPdb.downloadHandler.data;
reqPdb.Dispose();
//3.将加载的数据以流的形式(文件流或者内存流对象)传递给AppDomain对象中的LoadAssembly方法
//注意 这里使用流 不要用完就关 一定要等到热更相关内容不使用了 再关闭
dllStream = new MemoryStream(dll);
pdbStream = new MemoryStream(pdb);
//将我们两个文件的内存流用于初始化 appDomain 我们之后就可以通过该对象来执行我们对应的热更代码了
appDomain.LoadAssembly(dllStream, pdbStream, new PdbReaderProvider());
InitILRuntime();
ILRuntimeLoadOverDoSomthing();
//当ILRuntime启动完毕 想要在外部执行的内容
callBack?.Invoke();
}
}
}
Lesson03_练习题
using System;
using System.Collections;
using System.Collections.Generic;
using BaseFramework;
using UnityEngine;
public class Lesson03_练习题 : MonoBehaviour
{
void Start()
{
ILRuntimeManager.Instance.StartILRuntime((() => { print("启动ILRuntime成功"); }));
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com