notSerializableException当通过bundle中的kotlin函数在保存实例状态时



我将kotlin函数传递到 onSaveInstanceState中的 Bundle后,我得到了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() {
        ...
    }
}

问题是。您创建一个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可以通过基本使用内省来找到特定的隐藏字段并将其设置为空。我在某个地方有一个Kotlin示例,但是如果内部变更,那是脆弱的。

您还可以在任何班级之外声明您的lambda,以避免它成为内部班级。或确保包含类也是可序列化的。或用可序列化的静态类包装。其中之一可能会起作用,是否可以根据您想在lambda(和class(进行后序列化时想发生的事情。

如果您查看生成的字节码,则可以看到此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
     ...
}

最新更新