Java android中JSONArray抛出内存不足异常

Java android中JSONArray抛出内存不足异常,java,android,string,out-of-memory,arrays,Java,Android,String,Out Of Memory,Arrays,在我的应用程序中,我在一天结束时将一些数据同步到应用程序服务器。为此,我将所有数据包装为JSONObject的JSONArray。数据主要包括大约50张图片,每张图片大小约为50kb(以及一些文本数据)。所有这些图片都使用base64编码。上传图片时,一切都正常(以及一些文本数据)数量很少,但是当我上传大量图片时,比如说50张左右,我在日志中看到所有数据都正确地形成了JSONArray,但是当我尝试使用“array.toString()”方法显示JSONArray时,我遇到了内存不足异常。我认为

在我的应用程序中,我在一天结束时将一些数据同步到应用程序服务器。为此,我将所有数据包装为JSONObject的JSONArray。数据主要包括大约50张图片,每张图片大小约为50kb(以及一些文本数据)。所有这些图片都使用base64编码。上传图片时,一切都正常(以及一些文本数据)数量很少,但是当我上传大量图片时,比如说50张左右,我在日志中看到所有数据都正确地形成了JSONArray,但是当我尝试使用“array.toString()”方法显示JSONArray时,我遇到了内存不足异常。我认为这是由于堆已满(然而,当我尝试在清单中设置android:largeHeap=“true”时,一切都正常,但是我想避免使用这种方法,因为这不是一种好的做法)。我的目的只是将这个JSONArray值写入一个文件,然后将该文件分成小块并发送到服务器。 请告诉我将JSONAray值写入文件的最佳方法,这不会导致OOM问题。谢谢

以下是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"}]
以下是我的代码的主要片段:

            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;
        }
jsonobjectaux;
JSONArray数组=新的JSONArray();
.
.
//循环遍历游标中的每条记录
for(int i=0;i
问题出在这里。您正试图将整个数据集加载到内存中。并且内存不足

Android的JSON类(以及一些其他JSON库)被设计为获取Java对象(内存中),将其序列化为对象的解析树(例如
JSONObject
JSONArray
)(内存中),然后将该树转换为
字符串(内存中),并将其写入某个地方

特别是在您的情况下(目前),当它将解析树转换为
String
时,会出现内存不足的情况;
String
实际上是此时所需内存量的两倍

为了解决您的问题,您有几个不同的选择,我将提供3个:

  • 根本不要使用JSON。重构只是将文件和信息发送到服务器

  • 重构内容,以便一次只将X个图像读取到内存中,并具有多个输出文件。其中X是一些图像。请注意,如果图像大小变化很大/不可预测,则这仍然有问题

  • 切换到使用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();
    
                // Sync the values in DB to the server
                Log.d("SyncData", "Opening db to read files");
                SQLiteDatabase db = context.openOrCreateDatabase("data_monitor", Context.MODE_PRIVATE, null);
                db.execSQL("CREATE TABLE IF NOT EXISTS user_data(device_id VARCHAR,name VARCHAR,age VARCHAR,picture_time VARCHAR);");
                Cursor c = db.rawQuery("SELECT * FROM user_data", null);
    
                int count = c.getCount();
    
                if (count > 0) {
    
                    File file = new File(Environment.getExternalStorageDirectory().getPath(), "Pictures/UserFile/UserFile");
                    JsonFactory jsonfactory = new JsonFactory();
                    JsonGenerator jsonGenerator = null;
                    try {
                        jsonGenerator = jsonfactory.createJsonGenerator(file, JsonEncoding.UTF8);
                        jsonGenerator.writeStartObject();       
                        jsonGenerator.writeArrayFieldStart("user_data"); //Name for the JSONArray
                    } catch (IOException e3) {
                        e3.printStackTrace();
                    }
    
                    c.moveToFirst();
    
                    // Looping through each record in the cursor
                    for (int i = 0; i < count; i++) {               
    
                        try {
    
                            jsonGenerator.writeStartObject();  //Start of inner object '{'
                            jsonGenerator.writeStringField("device_id", c.getString(c.getColumnIndex("device_id")));
                            jsonGenerator.writeStringField("name", c.getString(c.getColumnIndex("name")));
                            jsonGenerator.writeStringField("age", c.getString(c.getColumnIndex("age")));
                            jsonGenerator.writeStringField("picture_time", c.getString(c.getColumnIndex("picture_time")));
    
    
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
    
                        // creating a fourth column for the input of corresponding image from the sd card
    
                        Log.d("SyncData", "Name of image - " + c.getString(c.getColumnIndex("picture_time")));
    
                            image = c.getString(c.getColumnIndex("picture_time")).replaceAll("[^\\d]", ""); //Removing everything except digits
                            Log.d("SyncData", "imagename - " + image);
    
                            File f = new File(Environment.getExternalStorageDirectory().getPath(), "Pictures/UserPic/" + image + ".jpg");
                            Log.d("SyncData", "------------size of " + image + ".jpg" + "= " + f.length());
                            String image_str;
    
                            if (!f.exists() || f.length() == 0) {
                                Log.d("SyncData", "Image has either size of 0 or does not exist");
                                try {
                                    jsonGenerator.writeStringField("picture", "Error Loading Image");
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            } else {
    
                                try {
    
                                    // Reusing bitmaps to avoid Out Of Memory
                                    Log.d("SyncData", "Image exists,encoding underway...");
                                    if (bitmap_reuse == 0) {    //ps : bitmap reuse was initialized to 0 at the start of the code,not included in this snippet
    
                                        // Create bitmap to be re-used, based on the size of one of the bitmaps
                                        mBitmapOptions = new BitmapFactory.Options();
                                        mBitmapOptions.inJustDecodeBounds = true;
                                        BitmapFactory.decodeFile(f.getPath(), mBitmapOptions);
                                        mCurrentBitmap = Bitmap.createBitmap(mBitmapOptions.outWidth, mBitmapOptions.outHeight, Bitmap.Config.ARGB_8888);
                                        mBitmapOptions.inJustDecodeBounds = false;
                                        mBitmapOptions.inBitmap = mCurrentBitmap;
                                        mBitmapOptions.inSampleSize = 1;
                                        BitmapFactory.decodeFile(f.getPath(), mBitmapOptions);
    
                                        bitmap_reuse = 1;
                                    }
    
                                    BitmapFactory.Options bitmapOptions = null;
    
                                    // Re-use the bitmap by using BitmapOptions.inBitmap
                                    bitmapOptions = mBitmapOptions;
                                    bitmapOptions.inBitmap = mCurrentBitmap;
    
                                    mCurrentBitmap = BitmapFactory.decodeFile(f.getPath(), mBitmapOptions);
    
                                    if (mCurrentBitmap != null) {
                                        ByteArrayOutputStream stream = new ByteArrayOutputStream();
                                        try {
                                            mCurrentBitmap.compress(Bitmap.CompressFormat.JPEG, 35, stream);
                                            Log.d("SyncData", "------------size of " + "bitmap_compress" + "= " + mCurrentBitmap.getByteCount());
    
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
    
                                        byte[] byte_arr = stream.toByteArray();
                                        Log.d("SyncData", "------------size of " + "image_str" + "= " + byte_arr.length);
    
                                        stream.close();
                                        stream = null;
    
                                        image_str = Base64.encodeToString(byte_arr, Base64.DEFAULT);
    
                                        jsonGenerator.writeStringField("picture", image_str);
    
                                    }
    
                                } catch (Exception e1) {
                                    e1.printStackTrace();
                                }
                            }
    
                        try {
                            jsonGenerator.writeEndObject();  //End of inner object '}'
                        } catch (JsonGenerationException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
    
                        c.moveToNext(); // Moving the cursor to the next record
                    }
    
                    try {
                        jsonGenerator.writeEndArray();      //close the array ']'
                        //jsonGenerator.writeStringField("file_size", "0");   // If need be, place another object here.
                        jsonGenerator.writeEndObject();     
    
                        jsonGenerator.flush();
                        jsonGenerator.close();
    
                    } catch (JsonGenerationException e1) {
                        e1.printStackTrace();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
    
                    c.close();
                    db.close();
                }