Skip to content

Commit 0294e5c

Browse files
committed
更新日志
1 parent 00e5c98 commit 0294e5c

File tree

1 file changed

+65
-6
lines changed

1 file changed

+65
-6
lines changed

docs/business/reload/commonerrors.md

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,72 @@
11
# 常见问题
22

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

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

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

9-
解决办法有几种:
10+
解决办法是将`List<T>`换成`T[]`
1011

11-
- 给被序列化或者反序列化的类型加上`[Serializable]`特性,如果这些类型中成员字段的类型也是class类型`A`或者`A[]`,则也要给`A`也加上`[Serializable]`
12-
- 修改这些反序列化库的代码,在卸载程序集后,清空它们的反射缓存。像Unity的JsonUtility是native实现,无法清空缓存,只能更换为其他Json库。
12+
注意,**仅仅是[Serializable]和MonoBhehaviour(ScriptableObject)有此限制**,其他普通类型的字段可以为任意类型。
13+
14+
## 卸载时存在非法引用了被卸载程序集中对象或者函数的问题
15+
16+
17+
### UnityEngine.AndroidJavaRunnableProxy
18+
19+
这个对象在CreteJavaProxy时就被创建出来,并且被引擎层持有了,Unity没有提供直接卸载办法。
20+
有一个清理办法,但是做不到立马清理,在卸载时仍然会报这种错误:
21+
22+
```csharp
23+
IntPtr handle = UnityEngine.AndroidJNIHelper.CreateJavaRunnable(xxx);
24+
```
25+
26+
记录下handle.然后不用时清理它:
27+
28+
```csharp
29+
UnityEngine.AndrodJNISafe.DelegateGlobalRef(handle);
30+
```
31+
32+
但这个操作只是在c#层不再持有这个java Runnable对象。 只有java gc时,才可能真正彻底释放这个 Runnable,此时才会释放C#层的AndroidJavaRunanbleProxy,然后再不会再引用这个被卸载的程序集中对象。
33+
34+
35+
目前只有一个有效解决办法:
36+
37+
不要直接传递 被卸载的程序集中的delegate 给 AndroidJavaRunnable,而是其他AOT 或者不卸载的程序集中的 一个
38+
代理类。 创建 AndroidJavaRunnable时也同时记录下这个代理类。在代理类中保存了指向 实际的被卸载的程序集中的delegate,在卸载前清理这个代码类的 delegate引用为null。代码类似这样:
39+
40+
```csharp
41+
class MyRunnableWrapper
42+
{
43+
private AndroidJavaRunnable _runnble;
44+
45+
public MyRunnableWrapper(AndroidJavaRunnable runnable)
46+
{
47+
_runnble = runnable;
48+
}
49+
50+
public void Run()
51+
{
52+
_runnble?.Invoke();
53+
}
54+
55+
public void Clean()
56+
{
57+
_runnble = null;
58+
}
59+
}
60+
61+
static void Register(AndroidJavaRunnable runnable)
62+
{
63+
var wrapper = new MyRunnableWrapper(runnable);
64+
65+
// 将这个传递给Java层
66+
var runner = UnityEngine.AndroidJNIHelper.CreateJavaRunnable(wrapper.Run);
67+
68+
// 卸载前清理
69+
wrapper.Clean();
70+
}
71+
```
1372

0 commit comments

Comments
 (0)