【问题标题】:Out of Memory when UrlEncodedFormEntity with an image当 UrlEncodedFormEntity 带有图像时内存不足
【发布时间】:2012-01-02 18:42:18
【问题描述】:

我正在尝试将数据发送到服务器。也就是一些字符串字段和一张图片,编码为base64,作为String传递,像这样:

ByteArrayOutputStream stream = new ByteArrayOutputStream();
        mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        byte[] byteArray = stream.toByteArray();
        //Cleaning memory
        try {
            stream.close();
            stream = null;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //Eecode base64
        nameValuePairs.add(new BasicNameValuePair(DatosDB.KEY_IMG, Base64.encodeToString(byteArray, Base64.DEFAULT)));
        byteArray = null;

然后,在准备 HTTP PUT 请求时:

 try{
        HttpClient httpclient = new DefaultHttpClient();
        //PUT
        HttpPut httpput = new HttpPut(KEY_121 + ruta);
        //The exception is thrown when executing next instruction
        httpput.setEntity(new UrlEncodedFormEntity(nameValuePairs));
        HttpResponse response = httpclient.execute(httpput);

        HttpEntity entity = response.getEntity();
        is = entity.getContent();

        }catch(Exception e){
        Log.e(TAG, "Error in http connection "+e.toString());
        }   

错误如下所示:

12-20 00:24:11.622: ERROR/AndroidRuntime(7499): FATAL EXCEPTION: main
12-20 00:24:11.622: ERROR/AndroidRuntime(7499): java.lang.OutOfMemoryError
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:95)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:140)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at java.lang.StringBuilder.append(StringBuilder.java:125)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at java.net.URLEncoder.encode(URLEncoder.java:109)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at org.apache.http.client.utils.URLEncodedUtils.encode(URLEncodedUtils.java:184)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at org.apache.http.client.utils.URLEncodedUtils.format(URLEncodedUtils.java:163)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at org.apache.http.client.entity.UrlEncodedFormEntity.<init>(UrlEncodedFormEntity.java:71)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at com.android.upvar.Http_Request.put(Http_Request.java:171)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at com.android.upvar.Http_Request.poi(Http_Request.java:108)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at com.android.upvar.NewPOI.putPOI(NewPOI.java:360)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at com.android.upvar.NewPOI.access$6(NewPOI.java:325)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at com.android.upvar.NewPOI$5.onClick(NewPOI.java:157)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at android.view.View.performClick(View.java:2538)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at android.view.View$PerformClick.run(View.java:9152)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at android.os.Handler.handleCallback(Handler.java:587)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at android.os.Handler.dispatchMessage(Handler.java:92)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at android.os.Looper.loop(Looper.java:130)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at android.app.ActivityThread.main(ActivityThread.java:3687)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at java.lang.reflect.Method.invokeNative(Native Method)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at java.lang.reflect.Method.invoke(Method.java:507)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
12-20 00:24:11.622: ERROR/AndroidRuntime(7499):     at dalvik.system.NativeStart.main(Native Method)
12-20 00:24:11.632: WARN/ActivityManager(3923):   Force finishing activity com.android.upvar/.NewPOI
12-20 00:24:11.636: ERROR/(3923): Dumpstate > /data/log/dumpstate_app_error

我不明白为什么会出现此异常。我只是发送从三星 Galaxay 相机拍摄的常规图像(肯定小于 16MB)。有什么想法吗?

编辑:我必须补充一点,第一块代码和第二块代码在不同的类上,所以第一块代码上的数据被传递给另一个类的对象,后者将它传递给另一个类的另一个对象具有 HTTP 请求的类。

【问题讨论】:

    标签: android image image-processing out-of-memory


    【解决方案1】:

    为您的实体使用流式传输,以便它仅消耗流所要求的较小缓冲区大小。 ByteArrayOutputStream 将所有数据保存在内存中。

    HTTPClient - Performance Guide

    尝试如下实现,它不会在内存对象中创建不必要的。以下可能不是完整的实现。玩得开心编码!

    class BitMapRequestEntity extends AbstractHttpEntity {
    
        private Bitmap bitmap;
    
        public BitMapRequestEntity(Bitmap mBitmap) {
            super();
            this.bitmap = mBitmap;
        }
    
        @Override
        public InputStream getContent() throws IOException,
                IllegalStateException {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public long getContentLength() {
            // TODO Auto-generated method stub
            return 0;
        }
    
        @Override
        public boolean isRepeatable() {
            // TODO Auto-generated method stub
            return false;
        }
    
        @Override
        public boolean isStreaming() {
            return true;
        }
    
        @Override
        public void writeTo(OutputStream outstream) throws IOException {
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outstream);
        }
    
    }
    try {
        org.apache.http.client.HttpClient httpclient = new DefaultHttpClient();
        // PUT
        HttpPut httpput = new HttpPut(KEY_121 + ruta);
        // The exception is thrown when executing next instruction
        httpput.setEntity(new BitMapRequestEntity(mBitmap));
        HttpResponse response = httpclient.execute(httpput);
    
        HttpEntity entity = response.getEntity();
        is = entity.getContent();
    
    } catch (Exception e) {
        Log.e(TAG, "Error in http connection " + e.toString());
    }
    

    【讨论】:

    • 流式传输会很好,但在您的示例中它在哪里?它的代码是什么?我有点困惑:S
    • 魔法发生在“BitMapRequestEntity”中的“writeTo(OutputStream outstream) throws IOException”方法上。
    • 我喜欢魔法 :) 那么当 PHP 服务器接收到它时,它是像往常一样接收它还是我必须使用其他技巧才能接收它?
    • @geeth 我无法在 PHP 中获取图像。我试过 $_POST,$_PUT。但我无法收到任何图像。你能告诉我在后端获取图像吗?
    • @Arvind07 您的 android 应用程序运行时不会抛出异常吗?您的服务器是否收到请求?尝试在 Android 客户端和 PHP 服务器中将请求方法更改为 POST。
    【解决方案2】:

    最后,解决办法是把图片压缩到50%,如下:

    mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
    

    它减小了图像大小并使其足够小以将其作为字符串发送。

    【讨论】:

      【解决方案3】:

      做完之后

      mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
      byte[] byteArray = stream.toByteArray();
      

      您在内存中有两个版本的图像,一个是位图,一个是字节数组。通过Bitmap.recycle() 清理前者可能会有所帮助。

      不请自来的建议:用于传输图像的“多部分表单数据”是否不符合您的目的?这非常节省内存。

      【讨论】:

      • 回收方法没有改变:S。我从未听说过多部分,但听起来不错。我在谷歌上查过,但我不确定我理解正确。那么,我不会添加值对,而是添加部分,其中之一就是图像?不是和以前一样吗?或者我应该以某种方式分块图像?我应该将图像添加为位图、文件还是字符串?你知道这方面有什么好的教程吗?
      • 这里还有一个教程:blog.tacticalnuclearstrike.com/2010/01/… Multipart mime 会将图像分块并按路径发送。由于在此实现中您使用的是文件正文而不是将整个图像读入内存,因此您将把“何时将图像加载到内存”部分留给系统。
      • 我现在明白了。但是服务器端呢,我需要修改我的 PHP 脚本来处理多部分的请求吗?还是保持不变?
      • 我可以发送多部分的图像,没有错误。但是现在我无法像以前那样从 PHP 中的 $_PUT 获取数据。多部分数据如何到达?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-13
      • 2011-10-04
      • 2014-09-15
      • 1970-01-01
      • 1970-01-01
      • 2013-12-20
      相关资源
      最近更新 更多