`

android端读取本地图片出现OutOfMemoryException

阅读更多

前些日子一直为图片内存溢出问题困扰着,查了N多资料,将google彻底翻遍了都没找到解决方案,就当我几乎绝望的时候意外发现了一位网友的一个 工具类,抱着最后一丝希望将代码co过来试了一把,结果令我喜出望外。嘿,解决了!暂不说多么欢喜了,听我慢慢道来这其中的前因后果吧!

   需求:下载时候将图片一并down下来,在空间里显示并支持离线观看

   第一个版本代码:

 

   //从本地读取图片
    public Bitmap getBitmapFromSD(String filename) {
        FileInputStream fi = null;
        BufferedInputStream bi = null;
        Bitmap bp = null;
        try {
            fi = new FileInputStream(filename);
            bi = new BufferedInputStream(fi);
            bp = BitmapFactory.decodeStream(bi);
        } catch (IOException e) {
            bp = null;
        } finally {
            try {
                if (bi != null) {
                    bi.close();
                }
                if (fi != null) {
                    fi.close();
                }
            } catch (IOException e) {
                bp = null;
            }
        }
        return bp;
    }
    问题出现了,由于显示的图片过大,所以会出现OutOfMemoryException。我就设想能否捕捉异常来回收图片再重新加载,于是欲从网上找解决办法,什么手动干预GC,什么将图片弱化什么使用弱引用保存图片,有些总结得特别好(http://mzh3344258.blog.51cto.com/1823534/804237),这些方法我一一尝试可问题仍然未解决。不断的OOM,不断的尝试recycle,错误倒是不出现,可一旦内存吃不消就会显示不了图片,出现的都是默认图片。最终我从网上找到如下工具类,助我很好的解决了此问题,具体网址忘记了(得谢谢那位网友啦(*^__^*) ),现在代码贴出来以便下次顺手拈来

    public final class BitMapUtil {

      private static final Size ZERO_SIZE = new Size(0, 0);
      private static final Options OPTIONS_GET_SIZE = new Options();
      private static final Options OPTIONS_DECODE = new Options();
      private static final byte[] LOCKED = new byte[0];

// 此对象用来保持Bitmap的回收顺序,保证最后使用的图片被回收
      private static final LinkedList CACHE_ENTRIES = new LinkedList(); 

// 线程请求创建图片的队列
     private static final Queue TASK_QUEUE = new LinkedList(); 

// 保存队列中正在处理的图片的key,有效防止重复添加到请求创建队列 

     private static final Set TASK_QUEUE_INDEX = new HashSet();  

// 缓存Bitmap
     private static final Map IMG_CACHE_INDEX = new HashMap();                         // 通过图片路径,图片大小

     private static int CACHE_SIZE = 20; // 缓存图片数量

   static {
     OPTIONS_GET_SIZE.inJustDecodeBounds = true;
  // 初始化创建图片线程,并等待处理
  new Thread() {
   {
    setDaemon(true);
   }

   public void run() {
    while (true) {
     synchronized (TASK_QUEUE) {
      if (TASK_QUEUE.isEmpty()) {
       try {
        TASK_QUEUE.wait();
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
      }
     }
     QueueEntry entry = TASK_QUEUE.poll();
     String key = createKey(entry.path, entry.width,
       entry.height);
     TASK_QUEUE_INDEX.remove(key);
     createBitmap(entry.path, entry.width, entry.height);
    }
   }
  }.start();

 }

 

 
 public static Bitmap getBitmap(String path, int width, int height) {
        if(path==null){
            return null;
        }
  Bitmap bitMap = null;
  try {
   if (CACHE_ENTRIES.size() >= CACHE_SIZE) {
    destoryLast();
   }
   bitMap = useBitmap(path, width, height);
   if (bitMap != null && !bitMap.isRecycled()) {
    return bitMap;
   }
   bitMap = createBitmap(path, width, height);
   String key = createKey(path, width, height);
   synchronized (LOCKED) {
    IMG_CACHE_INDEX.put(key, bitMap);
    CACHE_ENTRIES.addFirst(key);
   }
  } catch (OutOfMemoryError err) {
   destoryLast();
   System.out.println(CACHE_SIZE);
   return createBitmap(path, width, height);
  }
  return bitMap;
 }

 

 
 public static Size getBitMapSize(String path) {
  File file = new File(path);
  if (file.exists()) {
   InputStream in = null;
   try {
    in = new FileInputStream(file);
    BitmapFactory.decodeStream(in, null, OPTIONS_GET_SIZE);
    return new Size(OPTIONS_GET_SIZE.outWidth,
      OPTIONS_GET_SIZE.outHeight);
   } catch (FileNotFoundException e) {
    return ZERO_SIZE;
   } finally {
    closeInputStream(in);
   }
  }
  return ZERO_SIZE;
 }

 

 // ------------------------------------------------------------------ private Methods
 // 将图片加入队列头
 private static Bitmap useBitmap(String path, int width, int height) {
  Bitmap bitMap = null;
  String key = createKey(path, width, height);
  synchronized (LOCKED) {
   bitMap = IMG_CACHE_INDEX.get(key);
   if (null != bitMap) {
    if (CACHE_ENTRIES.remove(key)) {
     CACHE_ENTRIES.addFirst(key);
    }
   }
  }
  return bitMap;
 }

 

 // 回收最后一张图片
 private static void destoryLast() {
  synchronized (LOCKED) {
   String key = CACHE_ENTRIES.removeLast();
   if (key.length() > 0) {
    Bitmap bitMap = IMG_CACHE_INDEX.remove(key);
    if (bitMap != null && !bitMap.isRecycled()) {
     bitMap.recycle();
     bitMap = null;
    }
   }
  }
 }

 

 // 创建键
 private static String createKey(String path, int width, int height) {
  if (null == path || path.length() == 0) {
   return "";
  }
  return path + "_" + width + "_" + height;
 }

 

 // 通过图片路径,宽度高度创建一个Bitmap对象
 private static Bitmap createBitmap(String path, int width, int height) {
  File file = new File(path);
  if (file.exists()) {
   InputStream in = null;
   try {
    in = new FileInputStream(file);
    Size size = getBitMapSize(path);
    if (size.equals(ZERO_SIZE)) {
     return null;
    }
    int scale = 1;
    int a = size.getWidth() / width;
    int b = size.getHeight() / height;
    scale = Math.max(a, b);
    synchronized (OPTIONS_DECODE) {
     OPTIONS_DECODE.inSampleSize = scale;
     Bitmap bitMap = BitmapFactory.decodeStream(in, null,
       OPTIONS_DECODE);
     return bitMap;
    }
   } catch (FileNotFoundException e) {
                Log.v("BitMapUtil","createBitmap=="+e.toString());
   } finally {
    closeInputStream(in);
   }
  }
  return null;
 }
 
 // 关闭输入流
 private static void closeInputStream(InputStream in) {
  if (null != in) {
   try {
    in.close();
   } catch (IOException e) {
    Log.v("BitMapUtil","closeInputStream=="+e.toString());
   }
  }
 }

 

 // 图片大小
 static class Size {
  private int width, height;

  Size(int width, int height) {
   this.width = width;
   this.height = height;
  }

  public int getWidth() {
   return width;
  }

  public int getHeight() {
   return height;
  }
 }

 

 // 队列缓存参数对象
 static class QueueEntry {
  public String path;
  public int width;
  public int height;
 }
}

 

   在使用时我只调用了getBitmap方法,将需要设置的高度宽度以及本地图片路径传递过去就能自动返回bitmap给我,而且当捕捉到OOMError 的时候将LinkedList的最后一张图片也就是最先存的图片进行溢出并回收就大功告成,特别注意的是这里捕捉错误Exception是获取不到的,一 定要手动捕获OutOfMemoryError你才能进行处理(估计这些道理大家都懂得,所以不赘述啦,童鞋们加油!办法总比困难多o(∩_∩)o )

分享到:
评论

相关推荐

    jpegkit-android,Android的高效JPEG操作,无OutOfMemoryException风险。.zip

    JPEGKIT的出现主要是为了解决Android应用在处理大量或大尺寸JPEG图片时可能出现的内存溢出问题。它通过将LIJPEG Turbo C库桥接到Android环境中,实现了对JPEG图像的底层优化处理。LIJPEG Turbo是一个著名的、高效的...

    Net 内存溢出(System.OutOfMemoryException)的常见情况和处理方式总结

    在什么情况下会出现OutOfMemonryException呢? 在我们试图新建一个对象时,而垃圾收集器又找不到任何可用内存时被抛出,这种情况下我们是可以捕获该 异常的; 另一种情况是,CLR需要内存时,而却系统却不能提供,也会抛出...

    android framework面试题集

    OOM(OutOfMemoryException)是 Android 应用程序中一种常见的异常,发生 OOM 的原因是程序需要申请一段“大”内存,但是虚拟机没有办法及时地给到,即使做了 GC 操作以后。为了减少单个 APP 对整个系统的影响,...

    SQL Server出现System.OutOfMemoryException异常的解决方法

    标题中的“SQL Server出现System.OutOfMemoryException异常的解决方法”指的是在使用SQL Server时遇到的一种常见错误,即系统内存不足异常(System.OutOfMemoryException)。这种异常通常发生在SQL Server试图处理的...

    Android基础面试笔记

    #### 三、Android中OOM与图片三级缓存的关系 **图片三级缓存与OOM关系**: - 图片三级缓存是为了提高图片加载速度而设计的,包括内存缓存、磁盘缓存和网络缓存。虽然它有助于加快图片显示速度,但如果内存缓存占用...

    dotnet C# 应用程序进程创建太多线程将会抛出 OutOfMemoryException 异常.rar

    在.NET框架中,C#应用程序在执行过程...通过谨慎地创建和使用线程,可以避免不必要的内存压力,从而预防`OutOfMemoryException`的出现。在实际项目中,应结合具体场景,灵活应用这些知识,确保应用程序的稳定性和性能。

    AppScan Source测试ExploitMe Mobile Android Labs项目.docx

    如果输入流中没有这些终止符,readLine()会不断读取并扩大输入缓冲区,直至耗尽所有堆内存,导致`OutOfMemoryException`,进而使Java虚拟机(JVM)和应用程序崩溃,形成拒绝服务(Denial of Service, DoS)攻击。...

    简易的图片浏览器

    8. **异常处理**:为了提高程序的健壮性,我们需要添加适当的异常处理代码,如尝试加载图片时可能出现的FileNotFoundException或OutOfMemoryException。 9. **资源管理**:在程序运行过程中,确保正确释放占用的...

    给pdf加水印

    然而,在实际操作中,可能会遇到一些技术问题,如“System.OutOfMemoryException”异常和“未将对象引用设置到对象的实例”的错误。下面我们将详细探讨这些问题的原因及解决方法。 1. **“System....

    .net 一些无法catch的异常

    1.StackOverFlowException (一般来说这个不是真的堆栈不够了,而是你的代码出现了无线递归),如果你用throw new StackOverFlowException 还是可以catch的2.OutOfMemoryException (好像只有 box newarr newobj 才会抛...

    使用HttpWebRequest实现大文件上传

    然而,这种方式在处理大型文件时存在明显的局限性,即容易引发内存溢出异常(OutOfMemoryException)。原因是`WebClient`在上传文件时会一次性将整个文件读入内存再发送到服务器。本文旨在探讨如何使用`HttpWebRequest...

    c#常见错误处理的几种方法

    在 C# 中,错误处理是通过 try-catch 块来实现的,try 块中包含可能出现错误的代码,而 catch 块中包含错误处理代码。 在 C# 中,有多种类型的错误,每种类型都有其特定的处理方法。下面我们将对 C# 中常见的错误...

    hibernate性能优化.doc

    在 Hibernate 中,如果我们需要批量处理大量数据,例如插入 1000 000 条记录,就会出现 OutOfMemoryException 异常。这是因为 Hibernate 的 Session 有一个一级缓存,会将所有对象存储在内存中。如果不及时清空缓存...

    Java内存泄露解决方案

    Java 内存泄露 解决方案 outofmemoryException 从实践获取真理

    使用HttpWebRequest实现大文件上最新传

    4. **错误处理**:考虑到网络条件不稳定等因素,需要对可能出现的各种错误情况进行妥善处理,确保应用程序的稳定性和用户体验。 通过上述方法,不仅能够有效地解决大文件上传时内存不足的问题,还能够通过封装成...

    dotnet C# 如何使用 MemoryFailPoint 检查是否有足够的内存资源来执行操作.rar

    如果内存不足,`MemoryFailPoint`会抛出一个`OutOfMemoryException`,这样开发者就能提前捕获并处理这个问题,而不是让程序意外地崩溃。 下面我们将详细探讨如何在C#中使用`MemoryFailPoint`: 1. **创建...

    niit sm3 mt2

    2. 异步编程:VB.NET中提供异步操作的方法之一是BeginRead(),它用于非阻塞式读取文件或网络流,使程序可以在等待读取完成的同时执行其他任务。 3. 文件访问类型:在VB.NET中,文件访问类型包括顺序访问、随机访问...

Global site tag (gtag.js) - Google Analytics