ILRuntime第三課Delegate

​1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ILRuntime.Runtime.Enviorment;
using System.IO;
using ILRuntime.CLR.Method;
 
public delegate void TestDelegateMethod(int a);
public delegate string TestDelegateFunction(int a);
 
public class DelegateDemo : MonoBehaviour {
    public static TestDelegateMethod TestMethodDelegate;
    public static TestDelegateFunction TestFunctionDelegate;
    public static System.Action<string> TestActionDelegate;
 
    //AppDomain是ILRuntime的入口,最好是在一個單例類中保存,整個遊戲全局就一個,這裏爲了示例方便,每個例子裏面都單獨做了一個
    //大家在正式項目中請全局只創建一個AppDomain
    AppDomain appdomain;
 
    void Start()
    {
        StartCoroutine(LoadHotFixAssembly());
    }
 
    IEnumerator LoadHotFixAssembly()
    {
        //首先實例化ILRuntime的AppDomain,AppDomain是一個應用程序域,每個AppDomain都是一個獨立的沙盒
        appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
        //正常項目中應該是自行從其他地方下載dll,或者打包在AssetBundle中讀取,平時開發以及爲了演示方便直接從StreammingAssets中讀取,
        //正式發佈的時候需要大家自行從其他地方讀取dll
 
        //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        //這個DLL文件是直接編譯HotFix_Project.sln生成的,已經在項目中設置好輸出目錄爲StreamingAssets,在VS裏直接編譯即可生成到對應目錄,無需手動拷貝
#if UNITY_ANDROID
        WWW www = new WWW(Application.streamingAssetsPath + "/HotFix_TestProject.dll");
#else
        WWW www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_TestProject.dll");
#endif
        while (!www.isDone)
            yield return null;
        if (!string.IsNullOrEmpty(www.error))
            UnityEngine.Debug.LogError(www.error);
        byte[] dll = www.bytes;
        www.Dispose();
 
        //PDB文件是調試數據庫,如需要在日誌中顯示報錯的行號,則必須提供PDB文件,不過由於會額外耗用內存,正式發佈時請將PDB去掉,下面LoadAssembly的時候pdb傳null即可
#if UNITY_ANDROID
        www = new WWW(Application.streamingAssetsPath + "/HotFix_TestProject.pdb");
#else
        www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_TestProject.pdb");
#endif
        while (!www.isDone)
            yield return null;
        if (!string.IsNullOrEmpty(www.error))
            UnityEngine.Debug.LogError(www.error);
        byte[] pdb = www.bytes;
        using (System.IO.MemoryStream fs = new MemoryStream(dll))
        {
            using (System.IO.MemoryStream p = new MemoryStream(pdb))
            {
                appdomain.LoadAssembly(fs, p, new Mono.Cecil.Pdb.PdbReaderProvider());
            }
        }
 
        InitializeILRuntime();
        OnHotFixLoaded();
    }
 
    void InitializeILRuntime()
    {
        //下面這些註冊代碼,正式使用的時候,應該寫在InitializeILRuntime中
        //TestDelegateMethod, 這個委託類型爲有個參數爲int的方法,註冊僅需要註冊不同的參數搭配即可
        appdomain.DelegateManager.RegisterMethodDelegate<int>();
        //帶返回值的委託的話需要用RegisterFunctionDelegate,返回類型爲最後一個
        appdomain.DelegateManager.RegisterFunctionDelegate<intstring>();
        //Action<string> 的參數爲一個string
        appdomain.DelegateManager.RegisterMethodDelegate<string>();
 
        //TestDelegateMethod委託轉換器
        appdomain.DelegateManager.RegisterDelegateConvertor<TestDelegateMethod>((act) =>
        {
            return new TestDelegateMethod((a) =>
            {
                ((System.Action<int>)act)(a);
            });
        });
 
        appdomain.DelegateManager.RegisterDelegateConvertor<TestDelegateFunction>((act) =>
        {
            return new TestDelegateFunction((a) =>
            {
                return ((System.Func<int,string>)act)(a);
            });
        });
 
 
    }
 
    void OnHotFixLoaded() {
        Debug.Log("完全在熱更DLL內部使用的委託,直接可用,不需要做任何處理");
        appdomain.Invoke("HotFix_TestProject.DelegateTest""Initialize",null,null);
        appdomain.Invoke("HotFix_TestProject.DelegateTest""RunTest"null,null);
 
        Debug.Log("如果需要跨域調用委託(將熱更DLL裏面的委託實例傳到Unity主工程用), 就需要註冊適配器,不然就會像下面這樣");
 
        try {
            //適配器錯誤會消失,因爲在InitializeILRuntime中寫了委託適配器,但是會報轉換器的錯誤
            appdomain.Invoke("HotFix_TestProject.DelegateTest""Initialize2"nullnull);
            appdomain.Invoke("HotFix_TestProject.DelegateTest""RunTest2"nullnull);
        }
        catch (System.Exception ex) {
            Debug.LogError(ex.ToString());
        }
 
        //爲了演示,清除適配器緩存,實際使用中不要這麼做
        //ClearDelegateCache();
        //Debug.Log("這是因爲iOS的IL2CPP模式下,不能動態生成類型,爲了避免出現不可預知的問題,我們沒有通過反射的方式創建委託實例,因此需要手動進行一些註冊");
        //Debug.Log("首先需要註冊委託適配器,剛剛的報錯的錯誤提示中,有提示需要的註冊代碼");
        //Debug.Log("ILRuntime內部是用Action和Func這兩個系統內置的委託類型來創建實例的,所以其他的委託類型都需要寫轉換器");
        //Debug.Log("將Action或者Func轉換成目標委託類型");
 
        Debug.Log("我們再來在Unity主工程中調用一下剛剛的委託試試");
        TestMethodDelegate(789);
        var str = TestFunctionDelegate(098);
        Debug.Log("!! OnHotFixLoaded str = " + str);
        TestActionDelegate("Hello From Unity Main Project");
 
    }
 
    void ClearDelegateCache() {
        var type = appdomain.LoadedTypes["HotFix_TestProject.DelegateTest"];
        ILMethod m = type.GetMethod("Method", 1) as ILMethod;
        m.DelegateAdapter = null;
 
        m = type.GetMethod("Function", 1) as ILMethod;
        m.DelegateAdapter = null;
 
        m = type.GetMethod("Action", 1) as ILMethod;
        m.DelegateAdapter = null;
    }
}

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