ILRuntime的设计目标
1.尽可能的无缝接入现有项目。
2.运行行为跟原生保持一致。
3.调用原生接口的性能尽可能的高。
1.HelloWorld
HelloWorld.cs
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
| public class HelloWorld : MonoBehaviour { AppDomain appdomain;
System.IO.MemoryStream fs; System.IO.MemoryStream p; void Start() { StartCoroutine(LoadHotFixAssembly()); }
IEnumerator LoadHotFixAssembly() { appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
#if UNITY_ANDROID WWW www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.dll"); #else WWW www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll"); #endif while (!www.isDone) yield return null; if (!string.IsNullOrEmpty(www.error)) UnityEngine.Debug.LogError(www.error); byte[] dll = www.bytes; www.Dispose();
#if UNITY_ANDROID www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.pdb"); #else www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.pdb"); #endif while (!www.isDone) yield return null; if (!string.IsNullOrEmpty(www.error)) UnityEngine.Debug.LogError(www.error); byte[] pdb = www.bytes; fs = new MemoryStream(dll); p = new MemoryStream(pdb); try { appdomain.LoadAssembly(fs, p, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider()); } catch { Debug.LogError("加载热更DLL失败,请确保已经通过VS打开Assets/Samples/ILRuntime/1.6/Demo/HotFix_Project/HotFix_Project.sln编译过热更DLL"); }
InitializeILRuntime(); OnHotFixLoaded(); }
void InitializeILRuntime() { #if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE) appdomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId; #endif }
void OnHotFixLoaded() { appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest", null, null);
}
private void OnDestroy() { if (fs != null) fs.Close(); if (p != null) p.Close(); fs = null; p = null; }
void Update() {
} }
|
HotFix_Project.InstanceClass
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
| namespace HotFix_Project { public class InstanceClass { private int id;
public InstanceClass() { UnityEngine.Debug.Log("!!! InstanceClass::InstanceClass()"); this.id = 0; }
public InstanceClass(int id) { UnityEngine.Debug.Log("!!! InstanceClass::InstanceClass() id = " + id); this.id = id; }
public int ID { get { return id; } }
public static void StaticFunTest() { UnityEngine.Debug.Log("!!! InstanceClass.StaticFunTest()"); }
public static void StaticFunTest2(int a) { UnityEngine.Debug.Log("!!! InstanceClass.StaticFunTest2(), a=" + a); }
public static void GenericMethod<T>(T a) { UnityEngine.Debug.Log("!!! InstanceClass.GenericMethod(), a=" + a); }
public void RefOutMethod(int addition, out List<int> lst, ref int val) { val = val + addition + id; lst = new List<int>(); lst.Add(id); } } }
|
2.Invocation
appdomain直接调用
InstanceClass.cs
1 2 3 4 5 6
| Debug.Log("调用无参数静态方法"); appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest", null, null); Debug.Log("调用带参数的静态方法"); appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest2", null, 123);
|
HotFix_Project.InstanceClass
1 2 3 4 5 6 7 8 9 10
| public static void StaticFunTest() { UnityEngine.Debug.Log("!!! InstanceClass.StaticFunTest()"); }
public static void StaticFunTest2(int a) { UnityEngine.Debug.Log("!!! InstanceClass.StaticFunTest2(), a=" + a); }
|
这种方式性能消耗较大,会频繁使用类名和方法名查找。
通过IMethod调用方法
1 2 3 4 5 6
| IType type = appdomain.LoadedTypes["HotFix_Project.InstanceClass"];
IMethod method = type.GetMethod("StaticFunTest2", 1);
appdomain.Invoke(method, null, 123);
|
此方法参数会装箱拆箱,产生GC。
1 2 3 4 5
| using (var ctx = appdomain.BeginInvoke(method)) { ctx.PushInteger(123); ctx.Invoke(); }
|
利用压栈的没有GC的调用方式。
指定参数类型来获得IMethod
1 2 3 4 5 6 7
| IType intType = appdomain.GetType(typeof(int));
List<IType> paramList = new List<ILRuntime.CLR.TypeSystem.IType>(); paramList.Add(intType);
method = type.GetMethod("StaticFunTest2", paramList, null); appdomain.Invoke(method, null, 456);
|
实例化热更里的类
1 2 3
| object obj = appdomain.Instantiate("HotFix_Project.InstanceClass", new object[] { 233 });
object obj2 = ((ILType)type).Instantiate();
|
调用成员方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| method = type.GetMethod("get_ID", 0); using (var ctx = appdomain.BeginInvoke(method)) { ctx.PushObject(obj); ctx.Invoke(); int id = ctx.ReadInteger(); Debug.Log("!! HotFix_Project.InstanceClass.ID = " + id); }
using (var ctx = appdomain.BeginInvoke(method)) { ctx.PushObject(obj2); ctx.Invoke(); int id = ctx.ReadInteger(); Debug.Log("!! HotFix_Project.InstanceClass.ID = " + id); }
|
属性是特殊的函数,get_等同于属性ID的get值。
PushObject塞入参数,ReadInteger获取返回值。
调用泛型方法
1 2 3
| IType stringType = appdomain.GetType(typeof(string)); IType[] genericArguments = new IType[] { stringType }; appdomain.InvokeGenericMethod("HotFix_Project.InstanceClass", "GenericMethod", genericArguments, null, "TestString");
|
获取泛型方法的IMethod
1 2 3 4 5
| paramList.Clear(); paramList.Add(intType); genericArguments = new IType[] { intType }; method = type.GetMethod("GenericMethod", paramList, genericArguments); appdomain.Invoke(method, null, 33333);
|
调用带Ref/Out参数的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| method = type.GetMethod("RefOutMethod", 3); int initialVal = 500; using(var ctx = appdomain.BeginInvoke(method)) { ctx.PushObject(null); ctx.PushInteger(initialVal); ctx.PushObject(obj); ctx.PushInteger(100); ctx.PushReference(0); ctx.PushReference(1); ctx.Invoke(); List<int> lst = ctx.ReadObject<List<int>>(0); initialVal = ctx.ReadInteger(1);
Debug.Log(string.Format("lst[0]={0}, initialVal={1}", lst[0], initialVal)); }
|