如何将云端终端模型类序列化到Android文件系统中

4
我已成功创建了一个云端终端模型,可以轻松地从App Engine检索信息。为了减少往返次数并提供更快的用户体验,我已确定一个实例希望存储到本地存储中。
在我的应用程序的其余部分,我正在使用ObjectInputStream读写对象,例如:
FileInputStream fis = context.openFileInput("PRIVFILE");
ObjectInputStream ois = new ObjectInputStream(fis);
AppModelState s = (AppModelState) ois.readObject();

显然,这要求所有数据成员实现Serializable接口。Model类继承GenericJSON并不是“Serializable”,因此

    public final class ModelClass extends GenericJson {}

我可以手动创建一个可序列化的对象来映射模型; 但是,由于属性数量很多,这似乎非常业余。

我考虑的另一种选择是创建一个Serializable Object包装器,该包装器仅具有JSON字符串作为成员,并提供接受ModelClass作为参数的setter/getter方法,例如:

class AppModelState implements Serializable {
   private String modelClassJSON;

   public ModelClass getModelClass() {
      // generate a new ModelClass from the JSON
   }

   public void setModelClass(ModelClass c) {
      // extract the JSON for storage
   }

   .....
}

我感觉一定有更好的方法来解决这个问题,而且这个问题应该已经被解决了很多次,但是我没有找到任何相关资源。请提供意见。

2个回答

6

我正在按照你在问题中提到的方法进行操作。

由于Cloud Endpoints对象已经被序列化以便通过网络传输,因此它们也可以被序列化以便本地存储。更好的是,在Android 3.0或更高版本中,您甚至不需要导入任何库——它已经存在了!例如:

import com.google.api.client.extensions.android.json.AndroidJsonFactory;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonFactory;

private static final JsonFactory JSON_FACTORY = new AndroidJsonFactory();

public void putObject(String key, Object value) throws Exception {
    byte[] outputbytes = null;
    if (value instanceof GenericJson) {
        outputbytes = JSON_FACTORY.toByteArray(value);
    } else {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        ObjectOutputStream objectstream = new ObjectOutputStream(output);
        objectstream.writeObject(value);
        objectstream.close();
        outputbytes = output.toByteArray();
    }

    // persist "outputbytes" ...
}

public <T> T getObject(String key, Class<T> outclass) throws Exception {
    // retrieve saved bytes...
    byte[] valuebytes = ...

    if (valuebytes[0] == '{' && valuebytes[1] == '"' && valuebytes[valuebytes.length-1] == '}') {
        // Looks like JSON...
        return JSON_FACTORY.fromString(new String(valuebytes, "UTF-8"), outclass);
    } else {
        ByteArrayInputStream input = new ByteArrayInputStream(valuebytes);
        ObjectInputStream objectstream = new ObjectInputStream(input);
        Object value = objectstream.readObject();
        objectstream.close();
        return outclass.cast(value);
    }
}

请注意,默认的AndroidJsonFactory(至少在Android v4.3中)在序列化长字符串时非常慢。如果您遇到性能问题,请创建一个新的JacksonFactory。其他所有内容保持不变。
更新:如果您想序列化一个GenericJson对象列表,只需创建一个包含这些对象列表的GenericJson对象即可。例如:
import com.google.api.client.json.GenericJson;
import com.google.api.client.util.Key;

public static class PersistantJson extends GenericJson {
    @Key public int one;
    @Key public String two;
}

public static class PersistantJsonList extends GenericJson {
    @Key public List<PersistantJson> list = new ArrayList<PersistantJson>();
}

现在,您可以将所有“generate cloud endpoint client library”创建的类(即PersistantJson)对象添加到PersistantJsonList变量的.list元素中,然后将该变量传递给putObject()。请注意,这需要列表中的所有对象都是相同的类,以便反序列化知道类型(因为JSON序列化不记录类型)。如果使用List<Object>,则读回的是List<Map<String,Object>>,您需要手动提取字段。

@Brain,你知道如何反序列化Cloud Endpoints对象的列表吗? - AsafK
@AsafK,这是一个好问题。我之前没有尝试过,不得不稍微尝试一下才能让它正常工作。我已经更新了我的答案,包括我发现的内容。如果你的云端点库包含一个其他对象列表的对象,那么我认为它已经可以“正常工作”了(虽然我没有尝试过)。 - Brian White

1
我认为对于将与Endpoints一起使用的类进行标准Java序列化并不是很有效。问题在于序列化是二进制的,而HTTP通信是字符串的。
如果您自己进行HTTP通信而不是使用Endpoints,我认为您会遇到同样的问题。为了发送对象,您需要对其进行序列化(将字符串成员转换为二进制),然后必须将二进制转换回字符串。
因此,如果您使用的数据量不太大,最简单的方法可能是将对象存储为JSON。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接