3.Unity中启动ILR

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

×

喜欢就点赞,疼爱就打赏