java - 即使设置了serialVersionUID,反序列化也会引发InvalidClassException

标签 java android deserialization proguard serialversionuid

不久前,我发布了一个序列化/反序列化用户对象的应用程序。

public String serializeUser(final User user) {
    final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    try {
        final ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(user);
        objectOutputStream.close();
    } catch (final IOException exception) {
        ...
    }

    return new String(Base64.encode(byteArrayOutputStream.toByteArray(), DEFAULT));
}

public User deserializeString(final String userString) {

    final byte userBytes[] = Base64.decode(userString.getBytes(), DEFAULT);
    final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(userBytes);

    final ObjectInputStream objectInputStream;
    final User user;
    try {
        objectInputStream = new ObjectInputStream(byteArrayInputStream);
        user = (User) objectInputStream.readObject();
        objectInputStream.close();
    } catch (final IOException | ClassNotFoundException exception) {
        ...
    }

    return user;
}

该对象是这样实现的:

public class User implements Serializable {
    private String email;
    private String name;

    ...
}

然后,在修改我的对象(我添加了一个新字段)之后,我了解到必须设置 serialVersionUID 以防对象定义发生更改,否则反序列化器不会更改能够识别存储的对象(因为它将自动生成serialVersionUID)。所以我继续这样做:

public class User implements Serializable {
    private static final long serialVersionUID = 123L;

    ...
}

但是现在我已经重新发布了包含这些更改的应用程序,我不断收到错误报告,指示该对象无法反序列化:

Caused by: java.io.InvalidClassException: com.myproject.h.e; local class incompatible: stream classdesc serialVersionUID = 184861231695454120, local class serialVersionUID = -2021388307940757454

我非常清楚,设置新的串行版本将使任何以前的串行版本( link1link2 )失效,但事实并非如此。正如您所看到的,错误日志指向与我完全不同的 serialVersionUID(18486...-20213...)手动设置为我的 User 类 (123L)。

我错过了什么?

如果有任何相关性,我将使用带有默认配置的 Proguard。

最佳答案

经过谷歌搜索很长一段时间后,我偶然发现了这个问题:How to stop ProGuard from stripping the Serializable interface from a class 。因此,我继续深入研究 APK(在 Android Studio 中可以 analyze the APK),找到我的 User 类,并查看其字节码:

class public Lcom/myproject/h/e;
.super Ljava/lang/Object;
.source "User.java"

# interfaces
.implements Ljava/io/Serializable;


# annotations
...


# instance fields
.field private a:Ljava/lang/String;

.field private b:Ljava/lang/String;

...


# direct methods
...

如您所见,静态字段 serialVersionUID 找不到。所以我继续添加以下配置作为 documentation suggests :

[Applications] may contain classes that are serialized. Depending on the way in which they are used, they may require special attention. [...] Sometimes, the serialized data are stored, and read back later into newer versions of the serializable classes. One then has to take care the classes remain compatible with their unprocessed versions and with future processed versions. In such cases, the relevant classes will most likely have serialVersionUID fields. The following options should then be sufficient to ensure compatibility over time:

-keepnames class * implements java.io.Serializable`

-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

因此,如果我转到新生成的 APK 并检查对象的字节码,它现在指定不丢弃或重命名 serialVersionUID:

class public Lcom/myproject/model/User;
.super Ljava/lang/Object;
.source "User.java"

...

# static fields
.field private static final serialVersionUID:J


# instance fields
...

我希望应用程序在反序列化我的对象时不再遇到任何问题。

关于java - 即使设置了serialVersionUID,反序列化也会引发InvalidClassException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56205722/

相关文章:

java - Swing 今天还在使用吗?

java - 将基于 Spring XML 的配置转换为基于 Java 的配置

android - 如何处理 Picasso OutOfMemoryError?

c# - 使用 JSON.Net CustomCreationConverter

java - 有点迷路…… jackson 反序列化

java - openjdk-11.0.1_linux-x64_bin.tar url 不起作用(https ://download. java.net/java/GA/jdk11/13/GPL/openjdk-11.0.1_linux-x64_bin.tar.gz )

java - Spring MVC 是否在启动时初始化所有 Controller ?

android - WebView 泄露了整个 Activity——用一个简单的例子!

android - 为什么在 Android 2.3 上通过短信发送时 "@"被转换为 "¿"?

java - 序列化以反序列化订单信息