完美解决 android 网络下载图片 outofmemery 问题!

 

 问题说明:网络下大图片,遇到的outofmemery问题。

 

我做的需求是类似一个图片浏览器,负责接收一个地址,并把图片显示出来。我的思路是 从网络下载图片保存在内存,转成 Bitmap 从而在 ImageView 中显示。

好了 问题来了,在我下载图片时出现了 outofmemery 问题代码在于

 

InputStream imageDateStream = conn.getInputStream(); //从网络上接受一个输入流

 

 BufferedInputStream     in = new BufferedInputStream(imageDateStream);

 

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
              
               System.out.println("--------------begin----------");
               
               while ( (size = inputStream.read(buf)) != -1 ){  
                outputStream.write(buf, 0, size);        -----------------------问题就处在这里,爆出 outofmemery
               }
                  
               System.out.println("--------------------end----------------");

 

 

 

我就没有把下载的图片数据保存成文件,只是保存在 byte byteArray[] 内,

 

2、在获得byteArray[] 数据后 再显示图片的时候 再次遇到 outofmemery错误,解决方法是 减少像素的采集。代码如下:

byte byteArray[] = outputStream.toByteArray();
                             
               BitmapFactory.Options opts = new BitmapFactory.Options();
               opts.inJustDecodeBounds = true;
               BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length, opts);
              
               int image_size = computeSampleSize(opts, -1, ServerActivity.TOTAL_WIDTH * ServerActivity.TOTAL_HEIGHT );
              
               System.out.println("图片缩小的比例====" + image_size);
              
               opts.inSampleSize = image_size;
               opts.inJustDecodeBounds = false;
               try {
                imageBitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length, opts);
               } catch (OutOfMemoryError err) {
                   err.printStackTrace();
                  }

 

//动态计算取样的值
 public static int computeSampleSize(BitmapFactory.Options options,
         int minSideLength, int maxNumOfPixels) {
  
     int initialSize = computeInitialSampleSize(options, minSideLength,maxNumOfPixels);

     int roundedSize;
     if (initialSize <= 8 ) {
         roundedSize = 1;
         while (roundedSize < initialSize) {
             roundedSize <<= 1;
         }
     } else {
         roundedSize = (initialSize + 7) / 8 * 8;
     }

     return roundedSize;
 }

 private static int computeInitialSampleSize(BitmapFactory.Options options,
   int minSideLength, int maxNumOfPixels) {
    
  double w = options.outWidth;
     double h = options.outHeight;

     int lowerBound = (maxNumOfPixels == -1) ? 1 :
             (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
     int upperBound = (minSideLength == -1) ? 128 :
             (int) Math.min(Math.floor(w / minSideLength),
             Math.floor(h / minSideLength));

     if (upperBound < lowerBound) {
         // return the larger one when there is no overlapping zone.
         return lowerBound;
     }

     if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
         return 1;
     } else if (minSideLength == -1) {
         return lowerBound;
     } else {
         return upperBound;
     }
 }

 

//-----------------------------------------------------------------因为是把图片保存到内存,数据又大,使用完之后的清理是必须的。

 

个人觉得放在 finally

try{

//下载图片

 } catch (OutOfMemoryError err) {
                  err.printStackTrace();
                  
                  displaytoast("图片下载失败!");
                  System.out.println("出现 outofmemery异常,结束图片浏览器!");
                  clearBitmap();
                  finish();
                 }catch (Exception e) {
            e.printStackTrace();
           }finally {

            System.out.println("执行清理工作。。。。。");
         if(outputStream != null) {
          outputStream.flush();
          outputStream.close();
         }
         if(inputStream != null) {
          inputStream.close();
         }
         
         imageDateStream.close();
         conn.disconnect();
         
         System.out.println("断开连接。。。。。");
        }

//---------------------------------------------------------------------------图片使用完毕之后---------------------------------

 

if(imageBitmap != null && !imageBitmap.isRecycled() ){
      imageBitmap.recycle();   //回收图片所占的内存
            System.gc();  //提醒系统及时回收
     }
     
     imageBitmap = null;

 

 

不过很遗憾,我还是没能解决图片下载到内存时,遇到的outofmemery 问题,暂时用

try {

//下载

}catch (OutOfMemoryError err) {
                  err.printStackTrace();
                  
                  displaytoast("图片下载失败!");
                  System.out.println("出现 outofmemery异常,结束图片浏览器!");
                  clearBitmap();
                  finish();

}

 

这样做 只是保证软件不会意外退出,在发生outmemery 的时候

之间使用过计算可用内存的方法,限制超出可使用内存的图片下载,可惜没成功,原因是 内存剩余的空间还多得很,还是曝出了内存异常。

             long totalMemory = Runtime.getRuntime().maxMemory();
             long usedMemery = Runtime.getRuntime().totalMemory();

 

奇怪的是 多次浏览同几张图片,偶尔爆出outofmemery 并不是每次都会,郁闷....................,有知道解决方法的望留言告知。

 

--------------------------------------------------------------------------------问题新进展-----------------------------------------------------------------------------------

outofmemery报错是在 inputstream 转成 outputstream 时出错,很容易联想到 能否直接使用 inputstream,

 

具体代码是:Bitmap result = BitmapFactory.decodeStream(inputstream);

遗憾的是 这样写 图片一般要下载好几次才能成功一次,网上继续搜索发现以下描述:  

 

 

 这里Android开发网提醒大家,BitmapFactory类的decodeStream方法在网络超时或较慢的时候无法获取完整的数据,这里我们通过继承FilterInputStream类的skip方法来强制实现flush流中的数据,主要原理就是检查是否到文件末端,告诉http类是否继续。
static class FlushedInputStream extends FilterInputStream {
    public FlushedInputStream(InputStream inputStream) {
        super(inputStream);
    }

    @Override
    public long skip(long n) throws IOException {
        long totalBytesSkipped = 0L;
        while (totalBytesSkipped < n) {
            long bytesSkipped = in.skip(n - totalBytesSkipped);
            if (bytesSkipped == 0L) {
                  int byte = read();
                  if (byte < 0) {
                      break;  // we reached EOF
                  } else {
                      bytesSkipped = 1; // we read one byte
                  }
           }
            totalBytesSkipped += bytesSkipped;
        }
        return totalBytesSkipped;
    }
}

 

关于 输入流直接转成 图片 不完整的解决方案如下:

 

imageBitmap = BitmapFactory.decodeStream(new PatchInputStream(inputStream), null, opts);

其中 PathImputStream 是个内部类 代码如下:

//让inputstream 数据保持完整性
 public class PatchInputStream extends FilterInputStream {

  protected PatchInputStream(InputStream in) {
   super(in);
   // TODO Auto-generated constructor stub
  }

  public long skip(long n) throws IOException {
   long m = 0l;
   while (m < n) {
    long _m = in.skip(n - m);
    if (_m == 0l) {
     break;
    }
    m += _m;
   }
   return m;
  }

 }

最后采用的方案:

 

int inSampleSize = getPictureUsedecodeStream();

 downloadPictureUsedecodeStream( inSampleSize );

 

 

****************************************************************

 

/**
  * 直接使用inputstream下载图片,提取像素的比例是固定值
  *
  */
 private int getPictureUsedecodeStream() {
  
  int inSampleSize = 0;
  
  try {
   
      URL imageUrl = new URL( pictureURL ); 
      HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection();
      
      // 注意要设置超时,设置时间不要超过10秒,避免被android系统回收
      conn.setConnectTimeout(10*1000*60);   
      conn.setRequestMethod("GET");  
      conn.setDoInput(true);  
      conn.setDoOutput(true);    
      conn.setUseCaches(false);  
      conn.setRequestProperty("Connection", "Keep-Alive");  
      conn.connect();
      
      if (conn.getResponseCode() != 200) {
       
       System.out.println("getPictureUsedecodeStream-----请求url失败: ");
       throw new RuntimeException("getPictureUsedecodeStream--请求url失败");
       
      }else {   
       
       int netIconSize = conn.getContentLength();
       
       InputStream imageDateStream = conn.getInputStream();
       
       long totalMemory = Runtime.getRuntime().maxMemory();
             long usedMemery = Runtime.getRuntime().totalMemory();
            
       if( netIconSize <= 0 || (totalMemory - usedMemery) < netIconSize) { 
        imageDateStream.close();
        conn.disconnect();
       }else { //执行图片下载
        
        BufferedInputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        
           try {
          
               //获取网络输入流   
               inputStream = new BufferedInputStream(imageDateStream);
                
               BitmapFactory.Options opts = new BitmapFactory.Options();
               opts.inJustDecodeBounds = true;
               BitmapFactory.decodeStream(new PatchInputStream(inputStream), null, opts);
              
               inSampleSize = computeSampleSize(opts, -1, ServerActivity.TOTAL_WIDTH * ServerActivity.TOTAL_HEIGHT );
                            
           } catch (OutOfMemoryError err) {
            
           }finally {

            System.out.println("getPictureUsedecodeStream----执行清理工作。。。。。");
         if(outputStream != null) {
          outputStream.flush();
          outputStream.close();
         }
         if(inputStream != null) {
          inputStream.close();
         }
         
         imageDateStream.close();
         conn.disconnect();
         
         System.out.println("getPictureUsedecodeStream-------断开连接。。。。。");
        }
       }
      }
      } catch (Exception e) {
      
     }
     
     System.out.println("图片inSampleSize===" + inSampleSize);
     
  return inSampleSize;
 }

***********************************************************************************************

/**
  * 直接使用inputstream下载图片,提取像素的比例是固定值
  *
  */
 private void downloadPictureUsedecodeStream(int inSampleSize) {
  
  try {
   
      URL imageUrl = new URL( pictureURL ); 
      HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection();
      
      // 注意要设置超时,设置时间不要超过10秒,避免被android系统回收
      conn.setConnectTimeout(10*1000*60);   
      conn.setRequestMethod("GET");  
      conn.setDoInput(true);  
      conn.setDoOutput(true);    
      conn.setUseCaches(false);  
      conn.setRequestProperty("Connection", "Keep-Alive");  
      conn.connect();
      
      if (conn.getResponseCode() != 200) {
       
       System.out.println("getPictureUsedecodeStream-----请求url失败: ");
       throw new RuntimeException("downloadPictureUsedecodeStream--请求url失败");
       
      }else {   
       
       int netIconSize = conn.getContentLength();
       
       InputStream imageDateStream = conn.getInputStream();
       
       long totalMemory = Runtime.getRuntime().maxMemory();
             long usedMemery = Runtime.getRuntime().totalMemory();
            
       if( netIconSize <= 0 || (totalMemory - usedMemery) < netIconSize) { 
        imageDateStream.close();
        clearBitmap();
       }else { //执行图片下载
        
        BufferedInputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        
           try {
          
               //获取网络输入流   
               inputStream = new BufferedInputStream(imageDateStream);
                             
               BitmapFactory.Options opts = new BitmapFactory.Options();
               opts.inJustDecodeBounds = false;
               opts.inSampleSize = inSampleSize;
              
               imageBitmap = BitmapFactory.decodeStream(new PatchInputStream(inputStream), null, opts);
              
               if(null == imageBitmap ) {
                System.out.println("-----------下载的图片对象---imageBitmap为null");
               }
              
           } catch (OutOfMemoryError err) {
            
                  err.printStackTrace();
                  
                  displaytoast("图片下载失败OutOfMemoryError err!");
                  System.out.println("downloadPictureUsedecodeStream----出现 outofmemery异常,结束图片浏览器!");
                  clearBitmap();
                  finish();
                 }catch (Exception e) {
            e.printStackTrace();
           }finally {

            System.out.println("downloadPictureUsedecodeStream----执行清理工作。。。。。");
         if(outputStream != null) {
          outputStream.flush();
          outputStream.close();
         }
         if(inputStream != null) {
          inputStream.close();
         }
         
         imageDateStream.close();
         conn.disconnect();
         
         System.out.println("downloadPictureUsedecodeStream-------断开连接。。。。。");
        }
       }
      }
      } catch (Exception e) {
      System.out.println("从网络上获取图片失败!");
      e.printStackTrace();
      clearBitmap();
     }
  
 }
 

这样实现了 根据设备的屏幕来显示图片,并避免outofmemery错误,但是缺点是下载了两边图片【计算获取图片的 insamplesize 下载了一遍图片】,另一个缺点是不能下载bmp 格式的图片,bmp 格式只有使用 outputstream 循环读取图片的方法来下载图片了,代码如下:

把图片下载成 bitmap 图片的显示工作就简单了。

 

private void downloadPictureUseOutputStream() throws OutOfMemoryError {
  
  try {
   
      URL imageUrl = new URL( pictureURL ); 
      HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection();
      
      // 注意要设置超时,设置时间不要超过10秒,避免被android系统回收
      conn.setConnectTimeout(10*1000*60);   
      conn.setRequestMethod("GET");  
      conn.setDoInput(true);  
      conn.setDoOutput(true);    
      conn.setUseCaches(false);  
      conn.setRequestProperty("Connection", "Keep-Alive");  
      conn.connect();
      
      if (conn.getResponseCode() != 200) {
       
       System.out.println("请求url失败: ");
       throw new RuntimeException("请求url失败");
       
      }else {   
       
       int netIconSize = conn.getContentLength();
       
       System.out.println("图片大小:" + netIconSize);
       
       InputStream imageDateStream = conn.getInputStream();
       
       long totalMemory = Runtime.getRuntime().maxMemory();
             long usedMemery = Runtime.getRuntime().totalMemory();
            
       if( netIconSize <= 0 || (totalMemory - usedMemery) < netIconSize) { 
        imageDateStream.close();
        clearBitmap();
       }else {      //执行图片下载
        
        System.out.println("downloadPictureUseOutputStream--执行图片下载-------------");
        
        BufferedInputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        
           try {
            
               int BUFFER_SIZE = 4096;
               byte[] buf = new byte[BUFFER_SIZE];   
               int size = -1;   
            
               //获取网络输入流   
               inputStream = new BufferedInputStream(imageDateStream);
              
               //保存文件   
               outputStream = new ByteArrayOutputStream();
              
               System.out.println("--------------begin----------");
               
               while ( (size = inputStream.read(buf)) != -1 ){  
                outputStream.write(buf, 0, size);
               }
             
               System.out.println("--------------------end-------");
                             
               byte byteArray[] = outputStream.toByteArray();
                             
               BitmapFactory.Options opts = new BitmapFactory.Options();
               opts.inJustDecodeBounds = true;
               BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length, opts);
              
               opts.inSampleSize = computeSampleSize(opts, -1,
                 ServerActivity.TOTAL_WIDTH * ServerActivity.TOTAL_HEIGHT );;
               opts.inJustDecodeBounds = false;
               try {
                imageBitmap = BitmapFactory.decodeByteArray(byteArray, 0,
                  byteArray.length, opts);
               } catch (OutOfMemoryError err) {
                   err.printStackTrace();
                  }
              
           } catch (OutOfMemoryError err) {
            
                  err.printStackTrace();
                  
                  displaytoast("downloadPictureUseOutputStream---图片下载失败OutOfMemoryError err!");
                  System.out.println("downloadPictureUseOutputStream---出现 outofmemery异常,结束图片浏览器!");
                  clearBitmap();
                  throw new OutOfMemoryError();
                 }finally {

            System.out.println("downloadPictureUseOutputStream---执行清理工作。。。。。");
         if(outputStream != null) {
          outputStream.flush();
          outputStream.close();
         }
         if(inputStream != null) {
          inputStream.close();
         }
         
         imageDateStream.close();
         conn.disconnect();
         
         System.out.println("downloadPictureUseOutputStream---断开连接。。。。。");
        }

           System.out.println("downloadPictureUseOutputStream---downloadPicture--图片下载成功--!");
       }
      }
      } catch (Exception e) {
      System.out.println("downloadPictureUseOutputStream---从网络上获取图片失败!");
      e.printStackTrace();
      clearBitmap();
     }
 }

*********************************************************************

该方法偶尔会爆出outofmemery ,加上try catch 捕获内存溢出,这个error 也是没得办法的办法了。

 

 

你可能感兴趣的:(exception,android,网络,null,byte)