ILRuntime(一)

之前簡單的寫了個ILRuntime和Unity互相調用的文章:https://blog.csdn.net/wangjiangrong/article/details/90294366,感覺有蠻多不好的地方,所以想重新搞一搞,弄個簡單的ILRuntime和Unity的基本框架。

一些基本的概念在上面的文章講過了,這邊就懶得再說了,前期工作依舊是導入ILRuntime的庫到Unity,和一個Hotfix工程。

有興趣的同學可以看下這個工程,裏面有比較完整的代碼,也是後續講到UI相關的文章裏面的Demo工程。

 

補充:官方文檔說導入 Mono.Cecil.20,Mono.Cecil.Pdb,ILRuntime三個文件夾到Unity工程,但是在新下載的目錄中,根目錄下只有ILRuntime和Mono.Cecil兩個目錄,經測試,我們導入這兩個目錄,然後刪除ILRuntime/ILRuntime.csproj和Mono.Cecil子目錄中出了文件夾外的其他文件,同時刪除子文件夾內所有會導致Unity編譯報錯的AssemblyInfo.cs文件

 

Unity部分

我們先把ILRuntime部分的內容,例如綁定適配器,註冊委託等分成多個工具類,方便管理和查看,同時暴露出ILRuntime.Runtime.Enviorment.AppDomain實例,供外部使用。

註冊委託的類ILRuntimeDelegateHelp

using System;

namespace Tool
{
	public class ILRuntimeDelegateHelp
	{
        //跨域委託調用,註冊委託的適配器
        public static void RegisterDelegate(ILRuntime.Runtime.Enviorment.AppDomain appdomain)
        {
            //Button 點擊事件的委託註冊
            appdomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction>((act) =>
            {
                return new UnityEngine.Events.UnityAction(() =>
                {
                    ((Action)act)();
                });
            });
        }
    }
}

綁定適配器的類ILRuntimeAdapterHelp

namespace Tool
{
	public class ILRuntimeAdapterHelp
    {
        //跨域繼承綁定適配器
        public static void RegisterCrossBindingAdaptor(ILRuntime.Runtime.Enviorment.AppDomain appdomain)
        {
            //appdomain.RegisterCrossBindingAdaptor(new InterfaceIUIAdaptor());
        }
    }
}

讀取hotfix.dll的類ILRuntimeHelp

using System;
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

namespace Tool
{
	public class ILRuntimeHelp
	{
        public static ILRuntime.Runtime.Enviorment.AppDomain appdomain;
        static MemoryStream m_hotfixDllMemoryStream;
        static MemoryStream m_hotfixPdbMemoryStream;

        public static IEnumerator LoadILRuntime(Action LoadedFinish)
        {
            appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();

            //把官方文檔的WWW用UnityWebRequest替代了
            UnityWebRequest webRequest = UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/Hotfix.dll");
            yield return webRequest.SendWebRequest();

            byte[] dll = null;
            if (webRequest.isNetworkError)
            {
                Debug.Log("Download Error:" + webRequest.error);
            }
            else
            {
                dll = webRequest.downloadHandler.data;
            }
            webRequest.Dispose();

            webRequest = UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/Hotfix.pdb");
            yield return webRequest.SendWebRequest();

            byte[] pdb = null;
            if (webRequest.isNetworkError)
            {
                Debug.Log("Download Error:" + webRequest.error);
            }
            else
            {
                pdb = webRequest.downloadHandler.data;
            }

            //用下面的會報錯:ObjectDisposedException: Cannot access a closed Stream.
            /**
            using (MemoryStream fs = new MemoryStream(dll))
            {
                using (MemoryStream p = new MemoryStream(pdb))
                {
                    appdomain.LoadAssembly(fs, p, new Mono.Cecil.Pdb.PdbReaderProvider());
                }
            }
            **/
            m_hotfixDllMemoryStream = new MemoryStream(dll);
            m_hotfixPdbMemoryStream = new MemoryStream(pdb);
            appdomain.LoadAssembly(m_hotfixDllMemoryStream , m_hotfixPdbMemoryStream , new Mono.Cecil.Pdb.PdbReaderProvider());

            webRequest.Dispose();
            webRequest = null;

            ILRuntimeDelegateHelp.RegisterDelegate(appdomain);
            ILRuntimeAdapterHelp.RegisterCrossBindingAdaptor(appdomain);

            //用於ILRuntime Debug
            if (Application.isEditor)
                appdomain.DebugService.StartDebugService(56000);

            LoadedFinish?.Invoke();
        }
        
        public static void Dispose()
        {
            m_hotfixDllMemoryStream?.Dispose();
            m_hotfixPdbMemoryStream?.Dispose();
        }
    }
}

ILRuntime的類搞好之後,我們需要一個啓動類Launch,用來加載調用熱更的內容,同時管理熱更內容的生命週期。

using System;
using Tool;
using UnityEngine;

public class Launch : MonoBehaviour
{
    public static Action OnUpdate { get; set; }
    public static Action OnLateUpdate { get; set; }
    public static Action OnFixedUpdate { get; set; }
    public static Action OnApplicationQuitAction { get; set; }

    private void Start()
    {
        StartCoroutine(ILRuntimeHelp.LoadILRuntime(OnILRuntimeInitialized));
    }

    void OnILRuntimeInitialized()
    {
        ILRuntimeHelp.appdomain.Invoke("Hotfix.HotfixLaunch", "Start", null, null);
    }

    void Update()
    {
        OnUpdate?.Invoke();
    }

    void LateUpdate()
    {
        OnLateUpdate?.Invoke();
    }

    void FixedUpdate()
    {
        OnFixedUpdate?.Invoke();
    }

    void OnApplicationQuit()
    {
        OnApplicationQuitAction?.Invoke();

        ILRuntimeHelp.Dispose();
        GC.Collect();
    }
}

 

Hotfix部分

需要熱更的部分我們都需要放在Hotfix工程中,打成dll導入到Unity。我們需要一個主類提供一個接口給Unity調用,類似Main函數,然後在該類中去維護Hotfix部分的生命週期。例如

namespace Hotfix.Manager
{
    public interface IManager
    {
        void Init();
        void Start();
        void Update();
        void LateUpdate();
        void FixedUpdate();
        void OnApplicationQuit();
    }
}

一般一個項目會有多個功能模塊,例如UI,對象池,網絡等等,這些我們可以都用一個個的管理類來處理,我們先寫一個基類

namespace Hotfix.Manager
{
    public class ManagerBase<T> : IManager where T : IManager, new()
    {
        protected static T mInstance;

        public static T Instance
        {
            get
            {
                if (mInstance == null)
                {
                    mInstance = new T();
                }
                return mInstance;
            }
        }

        protected ManagerBase()
        {
        }

        public virtual void Init()
        {

        }

        public virtual void Start()
        {

        }

        public virtual void Update()
        {

        }

        public virtual void LateUpdate()
        {

        }

        public virtual void FixedUpdate()
        {

        }

        public virtual void OnApplicationQuit()
        {

        }
    }
}

然後需要的模塊繼承這個基類,例如

using UnityEngine;

namespace Hotfix.Manager
{
    class TimeManager : ManagerBase<TimeManager>
    {
        public override void Start()
        {
            base.Start();
            Debug.Log("TimeManager start");
        }
    }
}
using UnityEngine;

namespace Hotfix.Manager
{
    class UIManager : ManagerBase<UIManager>
    {
        public override void Start()
        {
            base.Start();
            Debug.Log("UIManager start");
        }
    }
}

接着我們就需要一個主類,去由unity調用,然後啓動我們的這些管理類。

using Hotfix.Manager;
using System;
using System.Collections.Generic;
using System.Linq;
using Tool;
using UnityEngine;

namespace Hotfix
{
    class HotfixLaunch
    {
        static List<IManager> managerList = new List<IManager>();

        public static void Start()
        {
            Debug.Log("HotfixLaunch Start");
            //獲取Hotfix.dll內部定義的類
            List<Type> allTypes = new List<Type>();
            var values = ILRuntimeHelp.appdomain.LoadedTypes.Values.ToList();
            foreach (var v in values)
            {
                allTypes.Add(v.ReflectionType);
            }
            //去重
            allTypes = allTypes.Distinct().ToList();

            //獲取hotfix的管理類,並啓動
            foreach (var t in allTypes)
            {
                try
                {
                    if (t != null && t.BaseType != null && t.BaseType.FullName != null)
                    {
                        if (t.BaseType.FullName.Contains(".ManagerBase`"))
                        {
                            Debug.Log("加載管理器-" + t);
                            var manager = t.BaseType.GetProperty("Instance").GetValue(null, null) as IManager;
                            manager.Init();
                            managerList.Add(manager);
                            continue;
                        }
                    }
                }
                catch (Exception e)
                {
                    Debug.LogError(e.Message);
                }
            }

            //綁定生命週期方法
            Launch.OnUpdate = Update;
            Launch.OnLateUpdate = LateUpdate;
            Launch.OnFixedUpdate = FixedUpdate;
            Launch.OnApplicationQuitAction = ApplicationQuit;

            foreach (var manager in managerList)
                manager.Start();
        }

        static void Update()
        {
            foreach(var manager in managerList)
                manager.Update();
        }

        static void LateUpdate()
        {
            foreach (var manager in managerList)
                manager.LateUpdate();
        }

        static void FixedUpdate()
        {
            foreach (var manager in managerList)
                manager.FixedUpdate();
        }

        static void ApplicationQuit()
        {
            Debug.Log("hotfix ApplicationQuit");
        }
    }
}

 

最後導出dll和pdb文件到streamassets目錄,將組件Launch掛載到場景中運行即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章