JSONArray android系统中抛出内存异常出 [英] JSONArray throwing out of memory exception in android

查看:600
本文介绍了JSONArray android系统中抛出内存异常出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用我在一天结束的应用同步,一些数据server.For此我总结我的所有数据JSONObjects.The的JSONArray数据主要包括各约50张图片,大小约50KB的(连同一些文本数据)。所有这些照片都设有使用Base64当图片上传(有一些文字资料一起encoding.Everthing正常工作)codeD数量很少,但是当我上传了大量没有图片的,说50左右然后我看到所有的数据正确形成的JSONArray日志,但是当我尝试使用我遇到内存不足的exception.This我相信'与Array.toString()方法来显示JSONArray由于堆快满了(但是,当我尝试让机器人​​:largeHeap =真的清单一切工作正常,但是我想避免使用此方法,因为这不是一个好的做法)。我的本意只是为了写这篇JSONArray值转换成文件,然后打破这个文件分成小块并在向服务器发送。
请指导我写JSONAray价值的文件,它不会导致OOM issues.Thanks的最好办法!

以下是JSONArray的格式为:

  [{PID:000027058451111,popup_time:2014年1月13日23时36分01秒,图片报:...... base64en codeD string......\",\"punching_time\":\"Absent\",\"status\":\"Absent\"},{\"pid\":\"000027058451111\",\"popup_time\":\"2014-01-13 23时36分21秒,图片报:...... base64en codeD串......,punching_time:缺席,状态:缺席}]

以下是我的code的主要片段:

 的JSONObject辅助;
            JSONArray阵列=新JSONArray();
            。
            。
            //通过游标每个记录循环
            的for(int i = 0; I<计数;我++){
                AUX =新的JSONObject();                尝试{
                    aux.put(PID,c.getString(c.getColumnIndex(PID)));
                    aux.put(地位,c.getString(c.getColumnIndex(身份)));
                    aux.put(pop_time,c.getString(c.getColumnIndex(pop_time)));
                    aux.put(punching_time,c.getString(c.getColumnIndex(punching_time)));
                    aux.put(图片报,c.getString(c.getColumnIndex(image_str))); //商店base64en codeD图片
                }赶上(例外五){
                    e.printStackTrace();
                }                array.put(辅助); //插入单个对象入阵,在这里工作完全正常,没有错误
                c.moveToNext(); //将光标移动到下一条记录
            }            Log.d(日志,JSON数组的长度 - + array.length()); //显示我在JSONArray一个JSONObjects的总量不,做工精细没有错误            //才行了OOM HERE
            //Log.d(\"Log,JSONArray是 - +与Array.toString());            如果(array.length()!= 0){
                尝试{                    字符串响应code =将writeToFile(数组); //编写JSONArray值到文件,然后将文件发送到服务器。                    如果(响应code.equals(200))
                        Log.d(日志,数据成功发送,从应用到应用服务器);
                    其他
                        Log.d(日志,数据未成功地从应用程序发送到应用服务器);
                }赶上(例外五){
                    e.printStackTrace();
                }
            }
            。
            。            私人字符串将writeToFile(JSONArray数据){            Log.d(日志,内部将writeToFile);
            文件externalStorageDir =新的文件(Environment.getExternalStorageDirectory()的getPath(),图片/文件​​);            如果(!externalStorageDir.exists()){
                externalStorageDir.mkdirs();
            }            字符串响应code =;
            文件数据文件=新的文件(externalStorageDir,文件);
    / * FileWriter的作家;
            字符串响应code =;
            尝试{
                作家=新的FileWriter(数据文件);
                writer.append(数据);
                writer.flush();
                writer.close();                响应code = sendFileToServer(dataFile.getPath(),AppConstants.url_app_server); //将文件发送到服务器,运行良好的几张照片            }赶上(IOException异常五){
                e.printStackTrace();
            } * /
            尝试{
                FileWriter的文件=新的FileWriter(存储/ sdcard0 /图片/文件/文件);                file.write(data.toString()); // GOT OOM在这里。
                file.flush();
                file.close();
                Log.d(日志,数据从JSONArray写到文件);
                响应code = sendFileToServer(dataFile.getPath(),AppConstants.url_app_server); //将文件发送到服务器,运行良好的几张照片
            }赶上(IOException异常五){
                e.printStackTrace();
            }            返回响应code;        }
        公共字符串sendFileToServer(字符串的文件名,字符串targetUrl这个){
            。
            。
            //将文件发送到服务器,运行良好的几张照片
            。
            。
            返回响应;
        }


解决方案

下面是个问题。你想你的整个数据集加载到内存中。而你正在运行内存不足。

Android的JSON类(和一些其他的JSON库)的设计采取了Java对象(在内存中),它序列化到对象的解析树(如的JSONObject JSONArray )(在内存中),那么这棵树转换为字符串(在内存中)和它的地方写出来。

具体你的情况(目前)它出现什么,当它在解析树转换成字符串它运行的内存;这字符串被提高了一倍的内存在该点所需的时间。

要解决您的问题,你有一些不同的选择,我会提供3:


  • 不要使用JSON的。重构简单的文件和信息发送到您的服务器。


  • 重构的东西,让你一次只能读出用X图像到内存中,并有多个输出文件。其中X是图像的一些数字。请注意,这仍是个问题,如果你的图像大小差异很大/不是predictable。


  • 切换到使用杰克逊作为一个JSON库。它支持流操作,你可以为你创建数组中的每个对象的流JSON输出文件。


编辑补充:作为您的code,它会看起来像这样使用杰克逊:

  //之前你在这里,已经创建了`File`对象
JsonFactory jsonfactory =新JsonFactory();
JsonGenerator jsonGenerator =
    jsonfactory.createJsonGenerator(文件,JsonEncoding.UTF8);jsonGenerator.writeStartArray();//注:我不知道C`是什么',但如果它是某种类型的光标它
//应该有一个规则hasNext()或类似的,你应该使用,而不是
// for循环
的for(int i = 0; I<计数;我++){    jsonGenerator.writeStartObject();    jsonGenerator.writeStringField(PID,c.getString(c.getColumnIndex(PID)));
    jsonGenerator.writeStringField(地位,c.getString(c.getColumnIndex(身份)));
    jsonGenerator.writeStringField(pop_time,c.getString(c.getColumnIndex(pop_time)));
    jsonGenerator.writeStringField(punching_time,c.getString(c.getColumnIndex(punching_time)));
    //商店base64en codeD图片
    jsonGenerator.writeStringField(图片报,c.getString(c.getColumnIndex(image_str)));    jsonGenerator.writeEndObject();    c.moveToNext(); //将光标移动到下一条记录
}jsonGenerator.writeEndArray();
jsonGenerator.close();

以上是未经测试,但它应该工作(或者至少让你在正确的方向前进)。

In my app I sync some data at the end of day to the app server.For this I wrap all my data as a JSONArray of JSONObjects.The data mainly includes about 50 pictures each with a size of approx 50kb(along with some text data).All these pictures are encoded using base64 encoding.Everthing works fine when the pictures uploaded(along with some text data) are few in number,but when I upload a large no of pictures ,say around 50 then I see in the logs that all the data is properly formed into the JSONArray,however when I try to display the JSONArray using 'array.toString()' method I encounter an out of memory exception.This I believe is due to the heap getting full(however,when I try making android:largeHeap="true" in the manifest everything is working fine,however I want to avoid using this approach,since this is not a good practice).My intention is just to write this JSONArray value into a file and then break this file into small chunks and send it across to the server. Please guide me of the best approach of writing the JSONAray value to the file which won't lead to OOM issues.Thanks !

Following is the format of the JSONArray:

[{"pid":"000027058451111","popup_time":"2014-01-13 23:36:01","picture":"...base64encoded string......","punching_time":"Absent","status":"Absent"},{"pid":"000027058451111","popup_time":"2014-01-13 23:36:21","picture":"...base64encoded string......","punching_time":"Absent","status":"Absent"}]

Following are the main snippets of my code:

            JSONObject aux;
            JSONArray array = new JSONArray();
            .
            .
            // Looping through each record in the cursor
            for (int i = 0; i < count; i++) {
                aux = new JSONObject();

                try {
                    aux.put("pid", c.getString(c.getColumnIndex("pid")));
                    aux.put("status", c.getString(c.getColumnIndex("status")));
                    aux.put("pop_time", c.getString(c.getColumnIndex("pop_time")));
                    aux.put("punching_time", c.getString(c.getColumnIndex("punching_time")));
                    aux.put("picture", c.getString(c.getColumnIndex("image_str"))); // stores base64encoded picture
                } catch (Exception e) {
                    e.printStackTrace();
                }

                array.put(aux); // Inserting individual objects into the array , works perfectly fine,no error here
                c.moveToNext(); // Moving the cursor to the next record
            }

            Log.d("Log", "length of json array - "+array.length()); // shows me the total no of JSONObjects in the JSONArray,works fine no error

            // HAD GOT OOM HERE
            //Log.d("Log", "JSONArray is - " + array.toString()); 

            if (array.length() != 0){
                try {

                    String responseCode = writeToFile(array);  //Writing the JSONArray value to file,which will then send file to server.

                    if(responseCode.equals("200"))
                        Log.d("Log","Data sent successfully from app to app server");
                    else    
                        Log.d("Log","Data NOT sent successfully from app to app server");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            .
            .

            private String writeToFile(JSONArray data) {

            Log.d("Log", "Inside writeToFile");
            File externalStorageDir = new File(Environment.getExternalStorageDirectory().getPath(), "Pictures/File");

            if (!externalStorageDir.exists()) {
                externalStorageDir.mkdirs();
            }

            String responseCode = "";
            File dataFile = new File(externalStorageDir, "File");
    /*      FileWriter writer;
            String responseCode = "";
            try {
                writer = new FileWriter(dataFile);
                writer.append(data);
                writer.flush();
                writer.close();

                responseCode = sendFileToServer(dataFile.getPath(), AppConstants.url_app_server); // Sends the file to server,worked fine for few pictures

            } catch (IOException e) {
                e.printStackTrace();
            }*/


            try {
                FileWriter file = new FileWriter("storage/sdcard0/Pictures/File/File");

                file.write(data.toString());        // GOT OOM here.
                file.flush();
                file.close();
                Log.d("Log","data  written from JSONArray to file");
                responseCode = sendFileToServer(dataFile.getPath(), AppConstants.url_app_server);    // Sends the file to server,worked fine for few pictures
            } catch (IOException e) {
                e.printStackTrace();
            }

            return responseCode;

        }


        public String sendFileToServer(String filename, String targetUrl) {
            .
            .
            // Sends the file to server,worked fine for few pictures
            .
            .
            return response;
        }

解决方案

Here's the issue. You're trying to load your entire dataset into memory. And you're running out of memory.

Android's JSON classes (and some other JSON libraries) are designed to take a Java object (in memory), serialize it to a parse tree of objects (e.g. JSONObject, JSONArray) (in memory), then convert that tree to a String (in memory) and write it out somewhere.

Specifically in your case (at the moment) it appears what when it converts the parse tree into a String it runs out of memory; That String is effectively doubling the amount of memory required at that point.

To solve your issue you have a few different choices, I'll offer 3:

  • Don't use JSON at all. Refactor to simply send files and information to your server.

  • Refactor things so that you only read X images into memory at a time and have multiple output files. Where X is some number of images. Note this is still problematic if your image sizes vary greatly / aren't predictable.

  • Switch to using Jackson as a JSON library. It supports streaming operations where you can stream the JSON to the output file as you create each object in the array.

Edit to add: for your code, it would look something like this using Jackson:

// Before you get here, have created your `File` object
JsonFactory jsonfactory = new JsonFactory();
JsonGenerator jsonGenerator = 
    jsonfactory.createJsonGenerator(file, JsonEncoding.UTF8);

jsonGenerator.writeStartArray();

// Note: I don't know what `c` is, but if it's a cursor of some sort it
// should have a "hasNext()" or similar you should be using instead of
// this for loop
for (int i = 0; i < count; i++) {

    jsonGenerator.writeStartObject();

    jsonGenerator.writeStringField("pid", c.getString(c.getColumnIndex("pid")));
    jsonGenerator.writeStringField("status", c.getString(c.getColumnIndex("status")));
    jsonGenerator.writeStringField("pop_time", c.getString(c.getColumnIndex("pop_time")));
    jsonGenerator.writeStringField("punching_time", c.getString(c.getColumnIndex("punching_time")));
    // stores base64encoded picture
    jsonGenerator.writeStringField("picture", c.getString(c.getColumnIndex("image_str")));

    jsonGenerator.writeEndObject();

    c.moveToNext(); // Moving the cursor to the next record
}

jsonGenerator.writeEndArray();
jsonGenerator.close();

The above is untested, but it should work (or at least get you going in the right direction).

这篇关于JSONArray android系统中抛出内存异常出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆