跳到主要内容

常见问题

[Serializable] 类型或者Monbehaviour(ScriptableObject)中包含泛型参数为被卸载程序集中类型的泛型字段时崩溃

使用JsonUtility或者MonoBehaviour序列化的对象的元数据信息会被引擎缓存。为了支持它们的卸载,我们复用了这些类型信息,即重新加载程序集后,引擎层访问到这些元数据信息会被重新更新为 最新的值。

由于缓存行为是引擎内部黑箱行为,我们无法复用太复杂的元数据,比如这些类型字段不能是List<T>,其中T是被卸载程序集中的类型。

解决办法是将List<T>换成T[]

注意,仅仅是[Serializable]和MonoBhehaviour(ScriptableObject)有此限制,其他普通类型的字段可以为任意类型。

卸载时存在非法引用了被卸载程序集中对象或者函数的问题

UnityEngine.AndroidJavaRunnableProxy

这个对象在CreteJavaProxy时就被创建出来,并且被引擎层持有了,Unity没有提供直接卸载办法。 有一个清理办法,但是做不到立马清理,在卸载时仍然会报这种错误:

IntPtr handle = UnityEngine.AndroidJNIHelper.CreateJavaRunnable(xxx);

记录下handle.然后不用时清理它:

UnityEngine.AndrodJNISafe.DelegateGlobalRef(handle);

但这个操作只是在c#层不再持有这个java Runnable对象。 只有java gc时,才可能真正彻底释放这个 Runnable,此时才会释放C#层的AndroidJavaRunanbleProxy,然后再不会再引用这个被卸载的程序集中对象。

目前只有一个有效解决办法:

不要直接传递 被卸载的程序集中的delegate 给 AndroidJavaRunnable,而是其他AOT 或者不卸载的程序集中的 一个 代理类。 创建 AndroidJavaRunnable时也同时记录下这个代理类。在代理类中保存了指向 实际的被卸载的程序集中的delegate,在卸载前清理这个代码类的 delegate引用为null。代码类似这样:

        class MyRunnableWrapper
{
private AndroidJavaRunnable _runnble;

public MyRunnableWrapper(AndroidJavaRunnable runnable)
{
_runnble = runnable;
}

public void Run()
{
_runnble?.Invoke();
}

public void Clean()
{
_runnble = null;
}
}

static void Register(AndroidJavaRunnable runnable)
{
var wrapper = new MyRunnableWrapper(runnable);

// 将这个传递给Java层
var runner = UnityEngine.AndroidJNIHelper.CreateJavaRunnable(wrapper.Run);

// 卸载前清理
wrapper.Clean();
}