2016-06-28 153 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註釋並創建數據庫幫助程序,這非常簡單。