Skip to content

Commit

Permalink
更新日志
Browse files Browse the repository at this point in the history
  • Loading branch information
pirunxi committed Aug 16, 2024
1 parent 00e5c98 commit 0294e5c
Showing 1 changed file with 65 additions and 6 deletions.
71 changes: 65 additions & 6 deletions docs/business/reload/commonerrors.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,72 @@
# 常见问题

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

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

程序集卸载后,会卸载所有类型元数据。而几乎所有常用的序列化库都会缓存类型的反射信息,这意味着如果你在代码中使用了Unity的JsonUtility或者LitJson之类的
序列库,它们会错误地缓存反射信息,导致你第二次(或者第三次)重新加载,并且反序列化时,会出错。
由于缓存行为是引擎内部黑箱行为,我们无法复用太复杂的元数据,比如这些类型字段不能是`List<T>`,其中T是被卸载程序集中的类型。

解决办法有几种:
解决办法是将`List<T>`换成`T[]`

- 给被序列化或者反序列化的类型加上`[Serializable]`特性,如果这些类型中成员字段的类型也是class类型`A`或者`A[]`,则也要给`A`也加上`[Serializable]`
- 修改这些反序列化库的代码,在卸载程序集后,清空它们的反射缓存。像Unity的JsonUtility是native实现,无法清空缓存,只能更换为其他Json库。
注意,**仅仅是[Serializable]和MonoBhehaviour(ScriptableObject)有此限制**,其他普通类型的字段可以为任意类型。

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


### UnityEngine.AndroidJavaRunnableProxy

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

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

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

```csharp
UnityEngine.AndrodJNISafe.DelegateGlobalRef(handle);
```

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


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

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

```csharp
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();
}
```

0 comments on commit 0294e5c

Please sign in to comment.