关于Android开发中Java对象序列化的一次测试

May 5 2015

Android开发过程中会遇到很多情况,需要将一个Java序列化,比如序列化一个对象来保存当前一些信息,等下次App启动后再还原。

对象序列化可以有好多种方法,一般最简单的就是实现Serializable接口,通过ObjectOutputStream写到SD卡中;还有可以将Java对象序列化到数据库中;还可以将Java对象转成一个字符串,把这个字符串写到SD卡中,反序列化时读取这个字符串,并且转成一个Java对象。

如果只保存一个Java对象,你应该不会把它写到数据库中的,一般会使用另外两种方法,但是那种方法好呢?

方法0:实现Serializable接口

写上两段代码

//将Object写入SD卡
private void writeObject(Object result) {
    ObjectOutputStream oos = null;
    FileOutputStream fileOutputStream = null;
    try {
        File file = new File(Config.getCachePath() + "bxbxbai");
        fileOutputStream = new FileOutputStream(file);
        oos = new ObjectOutputStream(fileOutputStream);
        oos.writeObject(result);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            fileOutputStream.close();
            oos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//读取Object
private Object readObject() {
    Object object = null;
    File file = new File(Config.getCachePath() + "bxbxbai");
    if (file.isFile()) {
        ObjectInputStream ois = null;
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(file);
            ois = new ObjectInputStream(fileInputStream);
            object = ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fileInputStream.close();
                ois.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return object;
    }
    return null;
}

##方法1:将对象转换成String序列化

//先用Gson将对象转换成String,然后把String写入SD卡
private void writeObjectString(Object result) {
    String s = new Gson().toJson(result);
    FileOutputStream fileOutputStream = null;
    try {
        File file = new File(Config.getCachePath() + "bxbxbai_string");
        fileOutputStream = new FileOutputStream(file);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fileOutputStream));
        writer.write(s);
        writer.flush();
        writer.close();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            fileOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//先读取String,然后转成Java对象
private Object readObjectString() {
    File file = new File(Config.getCachePath() + "bxbxbai_string");
    if (file.isFile()) {
        try {
            Scanner scanner = new Scanner(file);
            StringBuilder json = new StringBuilder();
            while (scanner.hasNext()) {
                json.append(scanner.nextLine());
            }
            scanner.close();
            return new Gson.fromJson(json.toString(), SerializeStringResult.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return null;
}

##测试

基本的功能代码已经都贴了,下面是测试代码:

private void test() {
    try {
        //省略创建object代码,result0 和result1 完全一样
        StopWatch.begin("writeObject");
        writeObject(result0);
        StopWatch.end("writeObject");

        StopWatch.begin("readObject");
        Object o1 = readObject();
        StopWatch.end("readObject");

        //写String
        StopWatch.begin("writeObjectString");
        writeObjectString(result1);
        StopWatch.end("writeObjectString");

        StopWatch.begin("readObjectString");
        Object o2 = readObjectString();
        StopWatch.end("readObjectString");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

测试数据是地址:地址,测试手机使用LG-D858

##测试结果(单位ms)

. writeObject readObject writeObjectString readObjectString
0 324 111 44 66
1 285 104 29 60
2 286 95 30 61
3 271 91 30 60
4 288 94 29 58
5 260 91 28 63
6 273 93 27 60
7 267 92 27 61
8 265 172 27 59
9 299 97 29 64
Average 282 104 30 61

一共测试了10次,平均结果如下:

  • Serializable写:282ms
  • Serializable读:104ms
  • 转String写:30ms
  • 转String读:61ms

也就是说将Java对象转成String再进行序列化以及反序列化可以大大减少时间。

Once upon a time, this used the standard Java Object{Input,Output}Stream, but the default implementation relies heavily on reflection (even for standard types) and generates a ton of garbage.

上面这段话是我几个月前我看Volley源码的时候看到的,在DiskBasedCache类中,大致的意思就是之前Volley也是用Java对象的序列化来缓存HTTP 头信息的,后来发现Object{Input,Output}Stream序列化严重依赖反射机制,会在序列化过程中产生大量的临时对象,从而更加频繁的触发GC,后来Google的工程师自己写了一个简单的序列化和反序列化方法

##总结

从测试结果中可以看到,将Java对象转换成String,然后在把String序列化可以节省大量的时间。不仅如此,Object{Input,Output}Stream序列化方法严重依赖Java反射机制,在序列化过程中会产生大量的临时对象,会更加频繁的触发GC操作

所以,我觉得还是把Java对象转成String再序列化吧,可以提升不少性能呢 :)

2015.05.07 更新

我这个是测试这两个序列化方法的性能。注意一下,Gson库将一个Java对象转成字符串也是使用Java反射的,如果某些场景需要更高的性能,那么需要自己写序列化方法。

比如,参考Parcelable接口,我们自己写代码,将对象转换成字符串(而不是依赖Gson库来转成字符串)序列化,以及相应的反序列化。或许你还想到了其他更好的方法,希望你能和我分享讨论~ :)