当我将 kotlin 函数传递给Bundle
in onSaveInstanceState
我得到了 NotSerializedException:
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = MyActivity$showFragmentA$1)
at android.os.Parcel.writeSerializable(Parcel.java:1447)
at android.os.Parcel.writeValue(Parcel.java:1395)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:665)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1330)
at android.os.Bundle.writeToParcel(Bundle.java:1079)
at android.os.Parcel.writeBundle(Parcel.java:690)
at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3269)
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3632)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:5728)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)
Caused by: java.io.NotSerializableException: MyActivity
at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1344)
at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1651)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1497)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1461)
at java.io.ObjectOutputStream.writeFieldValues(ObjectOutputStream.java:959)
at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:360)
at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1054)
at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1384)
at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1651)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1497)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1461)
at android.os.Parcel.writeSerializable(Parcel.java:1442)
at android.os.Parcel.writeValue(Parcel.java:1395)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:665)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1330)
at android.os.Bundle.writeToParcel(Bundle.java:1079)
at android.os.Parcel.writeBundle(Parcel.java:690)
at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3269)
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3632)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:5728)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)
我的课:
class MyActivity : Activity {
private var lastFragment: (() -> Fragment)? = null
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
outState?.putSerializable("lastFragment", lastFragment as Serializable)
}
fun showFragmentA() {
lastFragment = { FragmentA() }
// show fragment lastFragment()
}
fun showFragmentB() {
...
}
}
问题是这样的。您创建一个 lambda 并将其设置为函数的值lastFragment
。但什么是 lambda 呢?它是由编译器在MyActivity
班级。它是一个内部类,因此它有一个指向实例的指针MyActivity
这是不可序列化的。因此,您的函数实例(即 lambda 类)具有对阻止其序列化的内容的引用。看这个:
class MyClass {
var foo: (()->Unit)? = null
fun makeProblem() {
foo = { println("hi") }
}
}
这创造了MyClass$makeProblem$1
的内部类MyClass
容纳我的拉姆达的身体{ println("hi") }
...并且所有内部类都有指向其包含类的指针,因此MyClass$makeProblem$1
有一个类型变量MyClass
您看不到,但显然存在,因为这允许 lambda 中的代码访问包含类的成员。然后繁荣,这打破了序列化。
计划序列化 lambda 的库知道这一点,并且假设没有使用此内部类引用,则会剪切此链接的特殊情况。 Apache Spark 基本上通过使用内省来查找特定的隐藏字段并将其设置为 null 来实现此目的。我在某处有一个 Kotlin 示例,但如果内部发生变化,它会很脆弱。
您还可以在任何类之外声明 lambda,以避免它成为内部类。或者确保包含的类也是可序列化的。或者用可序列化的静态类包装它。其中之一可能有效,也可能无效,具体取决于稍后反序列化 lambda(和类)时您希望发生的情况。
如果查看生成的字节码,您可以看到这个 lambda 显然是一个内部类:
// ================uy/sotest/MyClass.class =================
// class version 50.0 (50)
// access flags 0x31
public final class uy/sotest/MyClass {
// access flags 0x2
// signature Lkotlin/jvm/functions/Function0<Lkotlin/Unit;>;
// declaration: kotlin.jvm.functions.Function0<kotlin.Unit>
private Lkotlin/jvm/functions/Function0; foo
@Lorg/jetbrains/annotations/Nullable;() // invisible
// access flags 0x11
// signature ()Lkotlin/jvm/functions/Function0<Lkotlin/Unit;>;
// declaration: kotlin.jvm.functions.Function0<kotlin.Unit> getFoo()
public final getFoo()Lkotlin/jvm/functions/Function0;
@Lorg/jetbrains/annotations/Nullable;() // invisible
...
// access flags 0x11
// signature (Lkotlin/jvm/functions/Function0<Lkotlin/Unit;>;)V
// declaration: void setFoo(kotlin.jvm.functions.Function0<kotlin.Unit>)
public final setFoo(Lkotlin/jvm/functions/Function0;)V
@Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0
...
// access flags 0x11
public final makeProblem()V
L0
LINENUMBER 7 L0
ALOAD 0
GETSTATIC uy/sotest/MyClass$makeProblem$1.INSTANCE : Luy/sotest/MyClass$makeProblem$1;
CHECKCAST kotlin/jvm/functions/Function0
PUTFIELD uy/sotest/MyClass.foo : Lkotlin/jvm/functions/Function0;
L1
LINENUMBER 8 L1
RETURN
L2
LOCALVARIABLE this Luy/sotest/MyClass; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
// access flags 0x1
public <init>()V
...
@Lkotlin/Metadata;( ... )
// access flags 0x18
final static INNERCLASS uy/sotest/MyClass$makeProblem$1 null null
// compiled from: ShowThing.kt
}
// ================uy/sotest/MyClass$makeProblem$1.class =================
// class version 50.0 (50)
// access flags 0x30
// signature Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function0<Lkotlin/Unit;>;
// declaration: uy/sotest/MyClass$makeProblem$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function0<kotlin.Unit>
final class uy/sotest/MyClass$makeProblem$1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function0 {
// access flags 0x1041
public synthetic bridge invoke()Ljava/lang/Object;
...
// access flags 0x11
public final invoke()V
...
// access flags 0x0
<init>()V
...
// access flags 0x19
public final static Luy/sotest/MyClass$makeProblem$1; INSTANCE
// access flags 0x8
static <clinit>()V
...
@Lkotlin/Metadata;( ... )
OUTERCLASS uy/sotest/MyClass makeProblem ()V
// access flags 0x18
final static INNERCLASS uy/sotest/MyClass$makeProblem$1 null null
...
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)