我需要从SQLite数据库中读取大量数据并创建格式正确的JSON。
我目前正在通过一个名为RequestPayload
的对象实现这一目标,该对象包含一些ArrayList
,其中我将从SQLite中读取的数据放入其中。
RequestPayload
类有parseJson()
方法返回JSONObject
,我最终调用toString()
方法获取最终写在文件上的JSON字符串
现在,当我在SQLite中获得“少量”数据时,一切都很顺利。当我获得大量数据时,会发生什么:
06-28 09:55:34.121 10857-6799/it.example.sampler E/AndroidRuntime: FATAL EXCEPTION: Thread-195240
Process: it.example.sampler, PID: 10857
Theme: themes:{com.cyanogenmod.trebuchet=overlay:system, com.android.settings=overlay:system, default=overlay:system, iconPack:system, fontPkg:system, com.android.systemui=overlay:system, com.android.systemui.navbar=overlay:system}
java.lang.OutOfMemoryError: Failed to allocate a 52962812 byte allocation with 16764752 free bytes and 41MB until OOM
at java.lang.StringFactory.newStringFromChars(Native Method)
at java.lang.AbstractStringBuilder.toString(AbstractStringBuilder.java:629)
at java.lang.StringBuilder.toString(StringBuilder.java:663)
at org.json.JSONStringer.toString(JSONStringer.java:430)
at org.json.JSONObject.toString(JSONObject.java:690)
at it.example.sampler.controllers.network.RequestBodyEncoder.serialise(RequestBodyEncoder.java:69)
at it.example.sampler.controllers.network.RequestBodyEncoder.createPacket(RequestBodyEncoder.java:50)
at it.example.sampler.services.FileStoreRunnable.run(FileStoreRunnable.java:57)
at java.lang.Thread.run(Thread.java:818)
06-28 09:55:34.509 10857-10857/it.example.sampler E/WindowManager: android.view.WindowLeaked: Activity it.example.sampler.ui.StartSamplingActivity has leaked window com.android.internal.policy.PhoneWindow$DecorView{74710d V.E...... R......D 0,0-1026,494} that was originally added here
at android.view.ViewRootImpl.<init>(ViewRootImpl.java:372)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:299)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:86)
at android.app.Dialog.show(Dialog.java:319)
at it.example.sampler.ui.StartSamplingActivity.executeStop(StartSamplingActivity.java:305)
at it.example.sampler.ui.StartSamplingActivity.onClickedButton(StartSamplingActivity.java:256)
at it.example.sampler.ui.StartSamplingActivity$3.onClick(StartSamplingActivity.java:190)
at android.view.View.performClick(View.java:5204)
at android.view.View$PerformClick.run(View.java:21158)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5461)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
错误行来自此方法:
private String serialise() throws JSONException {
// Serialise
String sampleString = mPayload.parseJSON().toString(); // THIS LINE
Logger.get().d(LOG_TAG, "Serialised payload: -> " + sampleString);
return sampleString;
}
如果我正确地获得了LogCat,那么错误就是在执行toString()
方法期间,看起来JSON对象太大了。
我该如何处理这种情况?
更新 回答@YashJain评论询问JSONObject的大小。
抽样后,持续2个小时,我有一个JSON包含:
float
s float
s float
个(和一个字符串)int
s 就字节而言,因为Float是由4个字节组成的(我希望我已经正确地完成了微积分)我已经大概了:
(12 * 155432 * 4) + (2 * 8393 * 4) + (1 * 8393 * 5) + (1 * 155432 * 4) ~= 8.191.573 bytes
更新
我被问及使用过的压缩算法。我通过ZLIB
和Deflater
Java类使用DeflaterOutputStream
。
因此流程是:
SQLite
SQLite
读取 - &gt;在内存中构建RequestPayload
对象RequestPayload
对象转换为JSONObject
(使用自定义解析器)。JSONObject
转换为字符串(使用JSONObject
toString()
方法)getBytes()
) - &gt;在Base64中编码 修改
为了解决“可能的重复标志”,我在研究过程中没有看到这个问题,因为它使用了largeHeap
指令(我没有使用)。也没有接受的答案,唯一的答案实际上并没有提供实际的解决方案。
答案 0 :(得分:1)
很抱歉没有提供更多反馈,我一直忙于其他工作,这在我的优先队列中排在最后。
无论如何,实际上对我有用的是使用Google GSON
库。
我的序列化方法变得简单:
private String serialise() {
// Serialise
return mPayload.toStringFromGSON();
}
班级toStringFromGSON()
中的方法Payload
是:
public String toStringFromGSON() {
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
return gson.toJson(this);
}
无论如何,我认为这只能算是临时补丁。事实上,我注意到应用程序使用的内存在此阶段会急剧增加,因此我认为这个问题只是推迟了,并且最终会在负载较重的情况下发生..
答案 1 :(得分:0)
你的Qn非常好。
您是否尝试过AsyncTask
或许它可以通过Thread
处理。
private String serialise() throws JSONException {
String sampleString = "";
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground( Void... voids ) {
// Serialise
sampleString = mPayload.parseJSON().toString(); // THIS LINE.
return null;
}
}.execute();
Logger.get().d(LOG_TAG, "Serialised payload: -> " + sampleString);
return sampleString;
}
我认为你有很好的知识。 对不起,如果它可能不起作用。但它确实在我的情况下。请检查一下。
答案 2 :(得分:0)
你会遇到巨大的Jsons转换问题,当转换为String时,系统会将整个内存量分配给一个唯一的变量,因此你会有内存溢出。
处理这种情况的最好方法是做一些Json Streaming,它将部分读取Json并解析他。
我认为重新发明轮子是没有意义的,所以我建议使用Json解析器库,比如Gson或Jackson,他们会自动在POJO中转换你的json,你可以将它们存储在SQLite数据库中,你可以像ORMLite一样使用一些数据库辅助。
这样,您可以收到一个巨大的JSON,直接将他解析为POJO对象,并将POJO直接插入数据库,将内存管理留给库。
在这个site中,您可以解析JSON并以GSON或Jackson格式检索整个类,之后,您只需要添加ORMLite注释并创建数据库助手,它真的是真的容易。