在保存实例状态时在捆绑包中传递 kotlin 函数时出现 NotSerializedException

2024-03-12

当我将 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(使用前将#替换为@)

在保存实例状态时在捆绑包中传递 kotlin 函数时出现 NotSerializedException 的相关文章

  • 在我的 Android 应用程序中使用 ServerValue.TIMESTAMP

    我读过很多相关的 stackoverflow 问题 ServerValue TIMESTAMP 但我不知道如何在我的应用程序中使用它 我需要获取帖子创建的时间戳 时间戳应该添加到与帖子的 uid 作者等相同的位置 代码片段其中写这篇文章Fi
  • 使用 dpi 与 dp 缩放图像之间的差异

    我拥有所有由九个补丁位图组成的 dpi 可绘制目录 xxhdpi 和 xxxhdpi 是否必要 可绘制目录中的可绘制资源文件可检索所有缩放的位图 并且我使用可绘制资源文件 现在 我的问题是我还根据大小 小 正常等 创建了 缩放 布局目录 其
  • 使用一个 apk 安装两个应用程序

    我有 2 个应用程序 1 内容提供者 2 使用此 ContentProvider 的应用程序 我需要使用单个 apk 文件安装这 2 个应用程序 我想在 Eclipse 中同时推送这两个应用程序 如果我将另一个项目添加到一个应用程序的构建路
  • 让协程等待之前的调用

    我还没有完全掌握 Kotlin 协程 基本上我希望协程在执行之前等待任何先前的调用完成 下面的代码似乎可以工作 但它正在做我认为它正在做的事情吗 private var saveJob Job null fun save saveJob s
  • 使用 HttpClient 时,为什么服务器响应中省略了 Content-Length 标头?

    我正在使用这个问题的源代码如何异步执行httprequest并显示下载响应的进度 https stackoverflow com questions 9594318 how to asynchronous perform a httpreq
  • 如何在 Android TextView 中使用土耳其语字符,如“ş ç ı ö”?

    我想在 android TextView 中写入 ile 但它没有正确绘制 怎样才能使用这样的字符呢 例如 我将文本视图设置为 ile 它显示为 ile 我怎样才能解决这个问题 尝试以下方法 看看是否有帮助 source http grou
  • android中根据屏幕尺寸计算图像尺寸

    我正在尝试根据屏幕尺寸计算图像高度和宽度 我从后端获取 5 x 7 尺寸的图像 为了将像素乘以 72 进行转换 我有 360 X 504 尺寸的图像 对于 360 X 504 我的动态透明矩形区域将显示为 1 223 x 1 179 即 8
  • 如何在活动中的必填字段中显示 * 符号

    我需要在活动中的必填字段中显示 符号 你能建议我怎样才能做到这一点吗 任何帮助 将不胜感激 我想说 作为必填字段的标记不遵循本机 Android 主题 的组合setHint and setError对于 Android 应用程序来说看起来更
  • onScale 事件后触发奇怪的 onScroll 事件

    我有一个同时使用 SimpleOnScaleGestureListener 和 SimpleOnGestureListener 的应用程序 每当我进行捏缩放时 我都会得到预期的 onScale 但是当我抬起时 我会看到一个奇怪的 onScr
  • 位图内存不足错误

    我对这个错误有疑问 我从 URL 制作网站图标解析器 我这样做是这样的 public class GrabIconsFromWebPage public static String replaceUrl String url StringB
  • Android:使 Dialog 周围的所有内容都比默认值更暗

    我有一个具有以下样式的自定义对话框 它显示了一个无边框对话框 后面的任何内容都会 稍微 变暗 我的设计师希望背后的一切都比 Android 的默认设置更暗 但不是完全黑色 有这样的设置吗 我能想到的唯一解决方法是使用全屏活动而不是对话框 只
  • 像 WhatsApp 一样发送图片

    我做了一个聊天应用程序 我想添加照片 文件共享我的应用程序中的概念与 WhatsApp 相同 我已经使用该应用程序制作了Xmpp Openfire目前我正在使用此功能进行照片共享 但它并不完全可靠 public void sendFile
  • 来自相机的 MediaCodec 视频流方向和颜色错误

    我正在尝试流式传输视频捕获直接从相机适用于 Android 设备 到目前为止 我已经能够从 Android 相机捕获每一帧预览帧 byte data Camera camera 函数 对数据进行编码 然后成功解码数据并显示到表面 我用的是安
  • 具有代理设置的 Android 模拟器 - 致命错误:.//android/base/sockets/ 检查失败:isValidFd(fd)。 FD 1404 最大1024

    需要使用代理设置运行模拟器 我在命令提示符中使用以下命令来启动模拟器 emulator avd AVD for 3 7 WVGA Nexus One http proxy http username password IP Port 如果没
  • TextInputLayout 对于在 EditText 中以编程方式给出提示没有效果

    我有一个 EditText 它的父级是 TextInputLayout 我试图以编程方式为 EditText 提供提示 不在布局中 在这种情况下 文本输入提示动画不起作用 它像简单的 EditText 一样工作 有人可以建议如何处理它吗 下
  • 如何在 Android 上将动态 alpha 遮罩应用于文本

    I want to make a dynamic alpha mask with drawable shapes as circles or whatever and apply it to a drawed text on Android
  • Android AdMob:addView 在返回活动之前不会显示广告

    我正在尝试在游戏顶部添加横幅广告 我的活动使用带有自定义 SurfaceView 的relativelayout 我希望广告与 SurfaceView 重叠 广告会加载并可点击 但不会绘制到屏幕上 当我离开活动并返回时 会绘制广告 例如 通
  • 使用Intent拨打电话需要权限吗?

    在我的一个应用程序中 我使用以下代码来拨打电话 Intent intent new Intent Intent ACTION CALL Uri parse startActivity intent 文档说我确实需要以下清单许可才能这样做
  • 如何在布局编辑器中模拟沉浸式模式

    我想在布局编辑器中全屏查看我的布局 我正在使用 eclipse 插件 我已经通过选择隐藏了 ActionBar NoActionBar组合中的主题 但导航栏是一个不同的故事 AFAIK 它只能使用代码中的标志来隐藏 我需要在活动 xml 文
  • 将焦距(以毫米为单位)转换为像素 - Android

    在 Android 中 我当前正在访问camera s焦距通过使用getFocalLength in Camera1 Camera2不是一个选择 我正在尝试完全填充当前的计算 focal length pix focal length m

随机推荐