在我将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)
Run Code Online (Sandbox Code Playgroud)
我的课:
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() {
...
}
}
Run Code Online (Sandbox Code Playgroud)
问题是这样的。您创建一个lambda并将其设置为函数的值lastFragment。但是什么是lambda?它是由编译器在MyActivity类内创建的自定义类。而且它是一个内部类,因此它有一个指向实例的指针,该实例MyActivity不可序列化。因此,您的函数实例lambda类具有对使其无法序列化的引用。看这个:
class MyClass {
var foo: (()->Unit)? = null
fun makeProblem() {
foo = { println("hi") }
}
}
Run Code Online (Sandbox Code Playgroud)
这创建了一个MyClass$makeProblem$1内部类MyClass来保存我的lambda的主体{ 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
...
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1403 次 |
| 最近记录: |