UH4L7——委托调用

本章代码关键字

1
2
3
appDomain.DelegateManager.RegisterFunctionDelegate<>()        //注册一种有参数的Action<>委托,以便于IL2CPP打包裁剪后还可以调用
appDomain.DelegateManager.RegisterMethodDelegate<>() //注册一种Func<>委托,以便于IL2CPP打包裁剪后还可以调用
appDomain.DelegateManager.RegisterDelegateConvertor<>() //将自定义委托转换为等效的Action<>委托或者Func<>委托,这样才能跨域调用自定义委托

委托调用

在委托的跨域调用中,如果出现Unity中自定义委托跨域关联ILRuntime中函数,需要进行:

  1. 注册委托(主要目的,避免IL2CPP打包裁剪报错)
  2. 注册委托转换器(主要目的,ILRuntime内部所有的委托都是以Action​或Func​来存储的)

注意:

  1. 委托的注册相关流程必须在主工程中完成
  2. 为了避免添加自定义委托转换器
    我们在使用委托时 尽量使用System​命名空间中的 Action​ 和 Func​,这样就不需要进行注册委托转换器了,只需要注册即可

在Unity中自定义委托后使用

  1. ILRuntime中委托成员 关联ILRuntime工程中函数

    直接常规使用即可,不会出现报错

    在Unity主工程内声明如下委托

    1
    2
    public delegate void MyUnityDel1();
    public delegate int MyUnityDel2(int i, int j);

    在ILRuntime热更新工程内可以直接使用相关内容

    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
    public class ILRuntimeMain
    {
    /// <summary>
    /// 把逻辑处理权交给热更新工程,这是一个启动函数
    /// </summary>
    public static void Main()
    {
    MyUnityDel1 fun = Fun1;
    fun();
    MyUnityDel2 fun2 = Fun2;
    int result = fun2(5, 6);
    Debug.Log(result);
    }

    public static void Fun1()
    {
    Debug.Log("IL_Fun1");
    }

    public static int Fun2(int a, int b)
    {
    Debug.Log("IL_Fun2");
    return a + b;
    }
    }

    输出:image

  2. Unity中委托成员 关联ILRuntime工程中函数

    直接关联会出现报错,这里就涉及到委托成员的跨域
    相当于Unity中的委托成员中存储了ILRuntime工程中的函数。就存在了跨域调用

    在Unity主工程的类内声明委托成员

    1
    2
    3
    4
    5
    6
    7
    8
    public delegate void MyUnityDel1();
    public delegate int MyUnityDel2(int i, int j);

    public class Lesson10 : MonoBehaviour
    {
    public MyUnityDel1 fun1;
    public MyUnityDel2 fun2;
    }

    ILRuntime热更工程内向Unity主工程内的类对象的委托成员添加热更工程内声明的方法并调用

    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
    using UnityEngine;

    namespace HotFix_Project
    {
    public class ILRuntimeMain
    {
    public static void Main()
    {
    //在Unity中声明委托成员,关联ILRuntime工程中的函数
    Lesson10 lesson10 = Camera.main.GetComponent<Lesson10>(); //对象挂载在了摄像机上
    lesson10.fun1 = Fun1;
    lesson10.fun1();

    lesson10.fun2 = Fun2;
    result = lesson10.fun2(7, 7);
    Debug.Log(result);
    }

    public static void Fun1()
    {
    Debug.Log("IL_Fun1");
    }

    public static int Fun2(int a, int b)
    {
    Debug.Log("IL_Fun2");
    return a + b;
    }
    }
    }

    输出:image

我们需要进行以下处理:可以通过报错信息中的提示,再进行初始化时进行代码的添加

主要有两部分:

  1. 注册委托(主要目的,避免IL2CPP打包裁剪报错)

    对于无参无返回值的委托,由于Action​本身就会被ILRuntime注册,因此不需要去注册

    假设要跨域调用 public delegate int MyUnityDel2(int i, int j)​ 这样的委托,
    或者跨域使用Func<int, int, int>()​,就需要使用RegisterFunctionDelegate​注册一个对应的Func<int, int, int>()

    假设要跨域调用有参无返回值的自定义委托,或者Action<..>​委托,就需要使用RegisterMethodDelegate​注册一个对应的Action<..>

    1
    2
    appDomain.DelegateManager.RegisterMethodDelegate<int>();        //假设有一个无返回值的int参数的委托
    appDomain.DelegateManager.RegisterFunctionDelegate<int, int, int>();
  2. 注册委托转换器(主要目的,ILRuntime内部所有的委托都是以Action<>​或Func<>​来存储的)

    我们需要将委托转换为对应的 Action<>​ 或 Func<>​,例如:

    假设要跨域调用 public delegate void MyUnityDel1()​ 这样的委托,就需要将其转换为 Action
    假设要跨域调用 public delegate int MyUnityDel2(int i, int j)​ 这样的委托,就需要将其转换为Func<int, int, int>

    其中,Func<int, int, int>​我们需要先注册,这样我们才能确保在IL2CPP打包后也可以调用

    转换的格式如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //​public delegate void MyUnityDel1()​
    appDomain.DelegateManager.RegisterDelegateConvertor<MyUnityDel1>((action) =>
    {
    return new MyUnityDel1(() =>
    {
    ((System.Action)action)();
    });
    });

    //​public delegate int MyUnityDel2(int i, int j)​
    appDomain.DelegateManager.RegisterDelegateConvertor<MyUnityDel2>((func) =>
    {
    return new MyUnityDel2((i, j) =>
    {
    return ((System.Func<int, int, int>)func)(i, j);
    });
    });

在将dll和pdb文件以流的形式供appDomain​读取后,在初始化appDomain​时,根据报错信息依次添加如下代码

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
//ILRuntime管理器内

//初始化ILRuntime相关的方法
private void InitILRuntime()
{
//委托转换器注册(要把自定义委托转换为Action或者Func)
appDomain.DelegateManager.RegisterDelegateConvertor<MyUnityDel1>((action) =>
{
return new MyUnityDel1(() =>
{
((System.Action)action)();
});
});
//委托注册
appDomain.DelegateManager.RegisterFunctionDelegate<int, int, int>();
//委托转换器注册(要把自定义委托转换为Action或者Func)
appDomain.DelegateManager.RegisterDelegateConvertor<MyUnityDel2>((func) =>
{
return new MyUnityDel2((i, j) =>
{
return ((System.Func<int, int, int>)func)(i, j);
});
});

//初始化ILRuntime相关信息(目前只需要告诉ILRuntime主线程的线程ID,主要目的是能够在Unity的Profiler剖析器窗口中分析问题)
appDomain.UnityMainThreadID = Thread.CurrentThread.ManagedThreadId;
}

输出:image

注意:

  1. 委托的注册相关流程必须在主工程中完成,在ILRuntime热更工程内无效
  2. 为了避免添加自定义委托转换器,
    我们在使用委托时,可以尽量使用 System​ 命名空间中的 Action<>​ 和 Func<>​,这样就不需要进行注册委托转换器了,只需要注册即可

在ILRuntime中自定义委托后使用

  1. ILRuntime中委托成员,关联ILRuntime工程中函数,直接使用即可

    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
    public delegate void MyILRuntimeDel1();
    public delegate int MyILRuntimeDel2(int i, int j);

    public class ILRuntimeMain
    {
    public static void Main()
    {
    MyILRuntimeDel1 ilFun1 = Fun1;
    ilFun1();
    MyUnityDel2 ilFun2 = Fun2;
    int ilResult = ilFun2(5, 6);
    Debug.Log(ilResult);
    }

    public static void Fun1()
    {
    Debug.Log("IL_Fun1");
    }

    public static int Fun2(int a, int b)
    {
    Debug.Log("IL_Fun2");
    return a + b;
    }
    }
  2. Unity中委托成员 关联ILRuntime工程中函数
    一般不会出现基础工程中,使用还无法预知的可变代码,所以我们不需要考虑这种情况