NotSerializableException在保存实例状态时在捆绑包中传递kotlin函数时 [英] NotSerializableException when pass kotlin function in bundle on saving instance state

查看:107
本文介绍了NotSerializableException在保存实例状态时在捆绑包中传递kotlin函数时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在将kotlin函数传递到onSaveInstanceState中的Bundle之后,我收到了NotSerializableException:

After I pass kotlin function into Bundle in onSaveInstanceState I got 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类具有对使其无法序列化的引用.看这个:

The problem is this. You create a lambda and set it as the value of your function lastFragment. But what is the lambda? It is a custom class created by the compiler within the MyActivity class. And it is an inner class, therefore it has a pointer to an instance of MyActivity which is not serializable. Therefore your instance of the function, the lambda class, has a reference to something that breaks it from being serialized. Look at this:

class MyClass {
    var foo: (()->Unit)? = null

    fun makeProblem() {
        foo = { println("hi") }
    }
}

这创建了MyClass$makeProblem$1MyClass$makeProblem$1内部类以保存我的lambda { println("hi") }的主体...并且所有内部类均具有指向其包含类的指针,因此MyClass$makeProblem$1具有类型为MyClass的变量您看不到,但显然在那里,因为这允许lambda中的代码访问包含类的成员.然后繁荣,这破坏了序列化.

This created MyClass$makeProblem$1 inner class of MyClass to hold the body of my lambda { println("hi") } ... and all inner classes have pointers to their containing class, so MyClass$makeProblem$1 has a variable of type MyClass that you cannot see but is obviously there because that allows the code in the lambda to access members of the containing class. Then boom, this breaks serialization.

计划对lambda进行序列化的库都知道这一点,并且在不使用此内部类引用的情况下,对此链接进行了特殊情况下的剪切. Apache Spark通过基本上使用自省来查找特定的隐藏字段并将其设置为null来做到这一点.我在某处有一个Kotlin示例,但是如果内部结构发生变化,它很脆弱.

Libraries that plan to serialize lambdas know about this and special case cutting of this link assuming there is no use of this inner class reference. Apache Spark does this, by basically using introspection to find a specific hidden field and setting it to null. I have a Kotlin example of this somewhere, but it is fragile in case the internals ever changed.

您还可以在任何类之外声明您的lambda,以避免它成为内部类.或确保包含的类也可序列化.或使用可序列化的静态类包装它.其中之一可能行不通,这取决于稍后对lambda(和类)进行反序列化时要发生的情况.

You can also declare your lambda outside of any class to avoid it from being an inner class. Or make sure the containing class is also serializable. Or wrap it with a static class that is serializable. One of these might work, or not, depending on what you want to happen when the lambda (and class) is deserialized later.

如果您查看生成的字节码,则可以看到此lambda显然是内部类:

If you look at the bytecode generated, you can see this lambda is clearly an inner class:

// ================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
     ...

}

这篇关于NotSerializableException在保存实例状态时在捆绑包中传递kotlin函数时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆