0

我正在关注developer.android网站here,我试图从url加载大图像(即1024 x 900)我的代码在我的Google Nexus中正常工作,但不适用于Nexus s 。大位图crushes(内存不足)

这里是我的代码

public static Bitmap getBitmapFromURL(String src) { 
     Bitmap bmImg; 
     URL myFileUrl = null; 

     try { 
      myFileUrl = new URL(src); 

      HttpURLConnection conn= (HttpURLConnection)myFileUrl.openConnection(); 
      conn.setDoInput(true); 
      conn.connect(); 
      InputStream is = conn.getInputStream(); 


      final BitmapFactory.Options options = new BitmapFactory.Options(); 

      BufferedInputStream bis = new BufferedInputStream(is, 4*1024); 
      ByteArrayBuffer baf = new ByteArrayBuffer(50); 
      int current = 0; 
      while ((current = bis.read()) != -1) { 
       baf.append((byte)current); 
      } 
      byte[] imageData = baf.toByteArray(); 

      BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options); 
      options.inJustDecodeBounds = true; 
      options.inSampleSize = calculateInSampleSize(options, 1024, 128); 
      options.inJustDecodeBounds = false; 
      Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length,options); 
      return bitmap; 
     } catch (Exception e) { 
      // TODO Auto-generated catch block 
      return null; 
     } 
    } 

public static int calculateInSampleSize(
      BitmapFactory.Options options, int reqWidth, int reqHeight) { 
     // Raw height and width of image 
     // Raw height and width of image 
     final int height = options.outHeight; 
     final int width = options.outWidth; 
     int inSampleSize = 1; 

     if (height > reqHeight || width > reqWidth) { 

      final int halfHeight = height/2; 
      final int halfWidth = width/2; 

      // Calculate the largest inSampleSize value that is a power of 2 and keeps both 
      // height and width larger than the requested height and width. 
      while ((halfHeight/inSampleSize) > reqHeight 
        && (halfWidth/inSampleSize) > reqWidth) { 
       inSampleSize *= 2; 
      } 
     } 

     return inSampleSize; 
    } 

的AsyncTask类

class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> { 
     private final WeakReference<ImageView> imageViewReference; 
     private String data = null; 

     public BitmapWorkerTask(ImageView imageView) { 
      // Use a WeakReference to ensure the ImageView can be garbage collected 
      imageViewReference = new WeakReference<ImageView>(imageView); 
     } 

     // Decode image in background. 
     @Override 
     protected Bitmap doInBackground(String... params) { 
      data = params[0]; 
      return Util.getBitmapFromURL(data); 
     } 

     // Once complete, see if ImageView is still around and set bitmap. 
     @Override 
     protected void onPostExecute(Bitmap bitmap) { 
      if (imageViewReference != null && bitmap != null) { 
       final ImageView imageView = imageViewReference.get(); 
       if (imageView != null) { 
        imageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, bitmap.getHeight()/2, bitmap.getWidth(), bitmap.getHeight()/2)); 
       } 
      } 
     } 
    } 

当我在Nexus S的我有内存溢出异常运行。如下面的android开发者网站,我不明白我在这里做错了什么,以及为什么我的应用程序在旧设备中崩溃。 请帮忙。

注意:我的图像宽度应始终为match_parent。这就是为什么我把calculateInSampleSize(选项,,128)

+0

http://stackoverflow.com/a/10652312/940834 – Doomsknight

回答

1

监守你设置inJustDecodeBounds你解码整个图像一次之后你是在浪费内存doInBackground方法中。 (我想你会在之后得到OOM)。考虑关闭你的输入流。

BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options); 
    options.inJustDecodeBounds = true; 
    options.inSampleSize = calculateInSampleSize(options, 1024, 128); 
    options.inJustDecodeBounds = false; 
    Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length,options); 
    return bitmap; 

我觉得你真的打算做的事:

options.inJustDecodeBounds = true; 
    BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options); 
    options.inSampleSize = calculateInSampleSize(options, 1024, 128); 
    options.inJustDecodeBounds = false; 
    Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length,options); 
    return bitmap; 

这将让图像的宽度和高度不存储位图(只得到选项),然后你子样本图像之后更正大小并返回位图。

我不明白你为什么在你的AsyncTask的onPostExexute再次调整位图,imageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, bitmap.getHeight()/2, bitmap.getWidth(), bitmap.getHeight()/2));

当你刚刚完成加载位图,为什么不加载正确的尺寸开始?(做上述变化,我建议你登录的尺寸,看后,如果你真的需要在onPostExexute再次调整)

你看图像加载库?

有不少可用的,一个似乎满足您的需求是Picasso

,人们可以做这样的事情:Picasso.with(context).load("url_to_image").fit().into(imageView);

此下载并缓存位图(在后台线程)和适合并绘制成一个imageView,全部在一行中,整齐!

+0

谢谢@Magnus的回答。你在这里做了很好的解释。这正是我在这里做错了options.inJustDecodeBounds = true;必须在decodeByteArray之前设置.... – fish40

+0

另外一件事现在我正在使用Aquery。它工作得很好。 – fish40

+0

如何在我的任务中调整图像大小我只能使用图像的一半而不是整个图像。这就是为什么。 – fish40

-1

在您的清单中:将android:largeHeap=true添加到您的应用程序选项卡。

+0

它并没有帮助我试过 – fish40

+0

这是隐藏真正的OutOfMemory问题在这里看到评论:http://stackoverflow.com/questions/20472094/outofmemoryerror-尽管-vm-have-enough-free-memory – Magnus

0

你必须做Bitmap提取Thread不在Main thread,做到这一点在AsyncTask否则你的应用程序将失去内存。

做的工作在你的getBitmapFromURL()工作Asynctask

+0

感谢@Arju的回答,请看看我编辑的问题。 – fish40

+1

为什么在一个后台线程上完成相同的分配解决了OOM问题? – Magnus