在我将kotlin函数传递到Bundle
中的onSaveInstanceState
后,我得到了NotSerializableException:
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() {
...
}
}
答案 0 :(得分:3)
问题是这个。您创建一个lambda并将其设置为函数lastFragment
的值。但是什么是lambda?它是由MyActivity
类中的编译器创建的自定义类。它是一个内部类,因此它有一个指向MyActivity
实例的指针,该实例不可序列化。因此,您的函数实例lambda类具有对其进行序列化的引用。看看这个:
class MyClass {
var foo: (()->Unit)? = null
fun makeProblem() {
foo = { println("hi") }
}
}
这创建了MyClass$makeProblem$1
内部类MyClass
来保存我的lambda { println("hi") }
的主体......并且所有内部类都有指向其包含类的指针,因此MyClass$makeProblem$1
有一个类型MyClass
的变量,你看不到但显然是因为它允许lambda中的代码访问包含类的成员。然后繁荣,这打破了序列化。
计划序列化lambdas的库知道这个和特殊情况切割这个链接,假设没有使用这个内部类引用。 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
...
}