内存不足错误,当将图像从安卓发送到php服务器时


OutOfMemoryError when sending image from android to php server

我试图将图像从我的SD卡发送到php服务器...我创建了 2 个类:第一个是 MainActivity.java第二个是 Base64.java它以 Base64 格式编码字节。

我的主要活动.java是:

public class MainActivity extends Activity {
    InputStream inputStream;
    @Override
public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.activity_main);
        Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/DCIM/Camera/1378889572299.jpg");
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream); //compress to which format you want.
        byte [] byte_arr = stream.toByteArray();
        String image_str = Base64.encodeBytes(byte_arr);
         ArrayList<NameValuePair> nameValuePairs = new  ArrayList<NameValuePair>();
        nameValuePairs.add(new BasicNameValuePair("image",image_str));
Log.e("ok", "1");
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://myserver.com/upload_image.php");
Log.e("ok", "3");
try {
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
Log.e("ok", "4");
HttpResponse response = httpclient.execute(httppost);
Log.e("ok", "5");
    String the_string_response = convertResponseToString(response);
} catch (IllegalStateException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
    }
    public String convertResponseToString(HttpResponse response) throws IllegalStateException, IOException{
        String res = "";
         StringBuffer buffer = new StringBuffer();
         inputStream = response.getEntity().getContent();
         final int contentLength = (int) response.getEntity().getContentLength(); //getting content length…..
          runOnUiThread(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(MainActivity.this, "contentLength : " + contentLength, Toast.LENGTH_LONG).show();                    
        }
    });
         if (contentLength < 0){
         }
         else{
                byte[] data = new byte[512];
                int len = 0;
                try
                {
                    while (-1 != (len = inputStream.read(data)) )
                    {
                        buffer.append(new String(data, 0, len)); //converting to string and appending  to stringbuffer…..
                    }
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
                try
                {
                    inputStream.close(); // closing the stream…..
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
                res = buffer.toString();     // converting stringbuffer to string…..
                final String res2 = res;
                runOnUiThread(new Runnable() {
                @Override
                public void run() {
                   Toast.makeText(MainActivity.this, "Result : " + res2, Toast.LENGTH_LONG).show();
                }
            });
                //System.out.println("Response => " +  EntityUtils.toString(response.getEntity()));
         }
         return res;
    }
}

我有这个错误:

........OutOfMemoryError
….at java.lang.AbstractStringBuilder.(AbstractStringBuilder.java:81)
….at java.lang.StringBuilder.(StringBuilder.java:68)
….at java.net.URLEncoder.encode(URLEncoder.java:98)
….at org.apache.http.client.utils.URLEncodedUtils.encode(URLEncodedUtils.java:184)
….at org.apache.http.client.utils.URLEncodedUtils.format(URLEncodedUtils.java:163)
….at org.apache.http.client.entity.UrlEncodedFormEntity.(UrlEncodedFormEntity.java:71)
….at com.example.imageuploadonserver.MainActivity.onCreate(MainActivity.java:43)

第 43 行是:httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

我对我的代码进行了一些更改...我将图像源从SD卡替换到drawabel.ic_luncher:

Bitmap bitmap = BitmapFactory.decodeFile(“/sdcard/DCIM/Camera/1378889572299.jpg”);

通过这个:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);

它成功地工作!!!

用谷歌搜索了很多,但要么我使用了错误的关键字,要么互联网上没有简单的解决方案。我希望这里有人可以帮助我。

提前致以最诚挚的问候和感谢,法德尔。

假设您的图像约为 5Mo,因为它是来自相机的图片。

  1. 当您将其加载为位图时,您的堆大小会增加 5Mo。
  2. 然后你把它转换成一个带有som压缩的字节数组,所以你的堆大小可能再次增长4Mo=>9Mo
  3. 然后你base64将其编码为一个字符串,堆再次增长4Mo =>13Mo
  4. 然后,通过将其作为字符串添加到 NameValu ̈Pair,我认为它复制了字符串(这里不确定),堆再次增长 => 17Mo
  5. 然后,当您将其添加为实体时,它会对内容进行编码,并且堆再次增长=> 22Mo

当您不再需要内存时,您应该尝试释放内存:

  1. 在步骤 2 之后使用 bitmap.recycle()。
  2. 在步骤 3 之后将咬合数组设置为 null
  3. 在步骤 4 之后将字符串设置为 null(不确定)
这样做

,您将释放大约 9 到 14Mo

但在所有情况下,通过对其进行编码(base64Encode 或 UrlEncode),堆大小将增长到位图大小的两倍。更好的方法应该是将文件的内容写入HTTP流将从文件中读取。

似乎您尝试上传的图像尺寸非常大。创建位图时需要缩小它。ic_launcher png的分辨率非常低,因此在创建位图时不会造成任何麻烦。

检查此链接以有效加载位图 -http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

希望它有帮助.. :)

为了避免一次将所有数据加载到内存中,我会使用 ChunkedOutputStream以块的形式流式传输数据。如果你小心一点,你可以上传无限大小的图像,因为它永远没有必要将整个东西放在内存中。

处理客户端<>服务器 Web 服务时,将整个请求或响应数据加载到 String 或任何内存对象中是一种不好的做法,应尽可能避免。在许多情况下,这是可以避免的。

为什么要避免呢?

因为在许多情况下,您永远无法保证数据的大小,并且通常会导致您出现 OOM 异常。

你应该怎么做?

对于大多数请求数据,您可以在发送它(XML、JSON、任何序列化)时构建它,并且不需要在内存中重新编辑整个结构即可对其进行序列化。

对于大多数应答数据,您可以解析流中的对象并将其保存在数据库中,或者丢弃不需要的流的任何部分。无需将整个响应加载为字符串。

在您的特定情况下,磁盘上有一个文件,它是您要压缩的图像。使用标准库或任何其他图像库将其转换为 90% JPEG 到另一个文件。然后,您可以将文件直接流式传输到 httpPost.entity 的输出流。

甚至

有可能某些库(甚至可能是标准库?)为您提供了直接从第一个图像文件中获取带有压缩图像的输入流的可能性。

这两种情况下,将您的第一个输入流封装在另一个执行 Base64 转换的输入流中,您就在那里。

这样,对于流缓冲区,您就永远不需要一次将超过几千字节加载到内存中。