2016-06-28 235 views
3

我需要从SQLite数据库中读取大量数据并创建格式正确的JSON。Android巨大的JSONObject toString去OutOfMemoryError

我目前通过一个名为RequestPayload的对象来实现这个功能,其中包含一些ArrayList s,其中我从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:

  • JSONArray含有155.432 float小号
  • JSONArray含有8.393含有float小号
  • JSONArray 8.393自定义对象每个包含5个float(和一个字符串)
  • 含有
  • 2D阵列155.432 int小号

在字节方面由于浮子是由4个字节(希望我已经做正确的演算)我有大约制成:

(12 * 155432 * 4) + (2 * 8393 * 4) + (1 * 8393 * 5) + (1 * 155432 * 4) ~= 8.191.573 bytes 

更新: 我被问到有关使用的压缩算法。我通过DeflaterDeflaterOutputStream Java类使用ZLIB

这样的流程是:

  • 样品数据 - >将它们储存在SQLite
  • 读自SQLite - >构建RequestPayload对象在内存
  • 转换RequestPayload对象JSONObject(自定义自定义解析器)。
  • 转换的JSONObject到字符串(使用JSONObjecttoString()方法)
  • 压缩字符串字节(使用getBytes()) - >对其进行编码以Base64
  • 发送最终的Base64串到服务器

编辑: 为了回应“可能重复的旗帜”,我没有看到这个问题在我的研究过程中没有帮助我,因为它使用了largeHeap指令(我没有使用)。也没有公认的答案,唯一的答案实际上并没有提供一个实际的解决方案。

+2

[JSONObject.toString()可能重复返回OutOfMemoryError]( http://stackoverflow.com/questions/32919833/jsonobject-tostring-returns-outofmemoryerror) – mdDroid

+0

我不知道答案,但我想知道它能够容纳多大的数据?像多少字节/字符一样。 –

+0

@mdDroid我已经添加了一个编辑,解释为什么我没有“可能重复”标志可能会有所帮助..无论如何谢谢标记,因为我没有看到它在研究之前问这个问题..可悲它没有帮助 – FredMaggiowski

回答

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); 
} 

无论如何,我认为这是可以考虑的只是一个临时的补丁。事实上,我注意到应用程序使用的内存在这个阶段急剧增加,因此我认为这个问题只会被推迟,最终会发生在较重的负载下。

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; 
} 

我假设你有很好的知识。 对不起,如果它可能不起作用。但它在我的情况。请检查一下。

+0

嗨,感谢您的答案,实际上这个工作是在'Runnable'实现内部实现的,并且在不同的'Thread'上运行: 'new Thread(new FileStoreRunnable(getApplicationContext(),mHandler))。start();' – FredMaggiowski

0

你曾经会遇到巨大的Jsons转换的问题,当转换为字符串时,系统会将全部内存分配给一个唯一的变量,因此你将有内存溢出。

处理这个问题的最好方法是做一些Json流式处理,它会读取部分JSON并解析它。我认为没有必要重新发明轮子,所以我建议使用Json解析器库,像Gson或Jackson,他们会自动转换你的json在POJO中,并且你可以将它们存储在SQLite数据库中,你可以使用一些数据库像ORMLite一样提供帮助。

这样,您可以收到巨大的JSON,将其直接解析为POJO对象,并将POJO直接插入数据库,并将内存管理留给库。

在这个site中,您可以解析您的JSON并以GSON或Jackson格式检索整个类,之后您只需添加ORMLite注释并创建数据库帮助程序,这非常简单。