软引用 弱引用 图片的加载与缓存

演示示例



public class MainActivity extends ListActivity {

    private TextView tv_info;

    private ImageView imageView;

    public static final String BIG_IMAGE_PATH = Environment.getExternalStorageDirectory().getPath() + "/big.jpg";

    public static final String SMALL_IMAGE_PATH = Environment.getExternalStorageDirectory().getPath() + "/small.png";

    private Bitmap smallBitmap;

    private Bitmap bigBitmap;

    private Bitmap weakBitmap;

    //定义一个HashMap,保存软引用的Bitmap对象,可防止OOM

    private Map<String, SoftReference<Bitmap>> imageCaches = Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());

    //弱引用可以在未被GC回收之前的获取到其中的Bitmap...一旦GC将其回收,那么就获取不到这个对象中的Bitmap了。该回收的时候,GC会自动的将其回收

    private WeakReference<Bitmap> weakReference;

    protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         String[] array = { "加载压缩后的大图到内存", "加载一张6000*8000(183M)的原图到内存", //                 "保存Bitmap的【SoftReference】到HashMap", "获得缓存到HashMap的Bitmap对象",//                 "保存Bitmap的【WeakReference】到HashMap", "获得WeakReference中的Bitmap对象", };         setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array))));         tv_info = new TextView(this);         tv_info.setTextColor(Color.BLUE);         tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);         tv_info.setPadding(20, 10, 20, 10);         getListView().addFooterView(tv_info);         imageView = new ImageView(this);         getListView().addFooterView(imageView);     }
    private int clickCount;     @Override     protected void onListItemClick(ListView l, View v, int position, long id) {         clickCount++;         if (clickCount % 2 == 0) {//大小图轮流显示             smallBitmap = BitmapFactory.decodeFile(SMALL_IMAGE_PATH);             imageView.setImageBitmap(smallBitmap);             tv_info.setText(formatInfo(smallBitmap));         } else {             switch (position) {             case 0:                 bigBitmap = decodeSampledBitmapFromFile(BIG_IMAGE_PATH, 1080, 1920);//每次都是重新加载的,从点击后响应速度就可以看得出来                 break;             case 1:                 bigBitmap = BitmapFactory.decodeFile(BIG_IMAGE_PATH);//图片有多大就实际加载多大。第一次还能扛得住,但第二次点击时就会OOM                 break;             case 2:                 addBitmapToCache(BIG_IMAGE_PATH);//只要第一次不OOM,以后点击(相当于替换掉旧的Bitmap)也都不会OOM                 return;//直接返回             case 3:                 bigBitmap = getBitmapFromCache(BIG_IMAGE_PATH);//直接从缓存中取得,可以看到点击后响应非常快                 break;             case 4:                 weakBitmap = BitmapFactory.decodeFile(BIG_IMAGE_PATH);                 weakReference = new WeakReference<Bitmap>(weakBitmap);//此后将weakBitmap置为null也不影响weakReference中的Bitmap                 return;//直接返回             case 5:                 bigBitmap = weakReference.get();//同样响应非常快                 break;             }             if (bigBitmap != null) {                 imageView.setImageBitmap(bigBitmap);                 tv_info.setText(formatInfo(bigBitmap));             } else Toast.makeText(this, "文件或缓存不存在", Toast.LENGTH_SHORT).show();         }     }
    private String formatInfo(Bitmap bitmap) {         StringBuffer buffer = new StringBuffer();         buffer.append("内存中bitmap的大小为:" + bitmap.getByteCount() + " = " + bitmap.getByteCount() / 1024 / 1024 + "M");         buffer.append("\n宽" + bitmap.getWidth() + " * 高" + bitmap.getHeight() + " * 参数" + 4 + " = " + bitmap.getWidth() * bitmap.getHeight() * 4);         return buffer.toString();     }     /**按照指定的大小加载图片到内存,实际大小基本上不可能是指定的这个大小,因为图片会按照自己的规则进行缩放*/     public Bitmap decodeSampledBitmapFromFile(String pathName, int reqWidth, int reqHeight) {         BitmapFactory.Options options = new BitmapFactory.Options();         options.inJustDecodeBounds = true;         BitmapFactory.decodeFile(pathName, options);         options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);         options.inJustDecodeBounds = false;         return BitmapFactory.decodeFile(pathName, options);     }     /**在保证解析出的bitmap宽高分别大于目标尺寸宽高的前提下,取可能的inSampleSize的最大值*/     public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {         int width = options.outWidth;         int height = options.outHeight;         tv_info.setText("原始图片的宽高:" + width + "-" + height);//3600-5400         int inSampleSize = 1;         if (height > reqHeight || width > reqWidth) {             int halfHeight = height / 2;             int halfWidth = width / 2;             while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {                 inSampleSize *= 2;             }         }         return inSampleSize;     }
    /**添加软引用Bitmap到Map中,使其缓存*/     public void addBitmapToCache(String path) {         imageCaches.put(path, new SoftReference<Bitmap>(BitmapFactory.decodeFile(path)));     }     /** 从缓存中取软引用的Bitmap对象*/     public Bitmap getBitmapFromCache(String path) {         SoftReference<Bitmap> softBitmap = imageCaches.get(path);         if (softBitmap != null) {//如果软引用还存在,那么直接就可以获取这个对象的相关数据...这样就实现了cache...             return softBitmap.get();         } else {//如果已经不存在,表示GC已经将其回收,我们需要重新实例化对象,获取数据信息(一般直接返回null)             //addBitmapToCache(path);             //return getBitmapFromCache(path);             return null;         }     }     public void clearCache() {         imageCaches.clear();     } }

简介


Java从JDK1.2版本开始,就把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。

这四种级别由高到低依次为:强引用、【软引用】、弱引用和虚引用。

这里重点介绍一下软引用和弱引用。

如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。

软引用可用来实现内存敏感的高速缓存。

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

弱引用与软引用的根本区别在于:只具有弱引用的对象拥有更短暂的生命周期,可能随时被回收;而只具有软引用的对象只有当内存不够的时候才被回收,在内存足够的时候,通常不被回收。

在java.lang.ref包中提供了几个类:SoftReference类、WeakReference类和PhantomReference类,它们分别代表软引用、弱引用和虚引用。ReferenceQueue类表示引用队列,它可以和这三种引用类联合使用,以便跟踪Java虚拟机回收所引用的对象的活动。

在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。

下面以使用软引用为例来详细说明。弱引用的使用方式与软引用是类似的。

假设我们的应用会用到大量的默认图片,比如应用中有默认的头像,默认游戏图标等等,这些图片很多地方会用到。如果每次都去读取图片,由于读取文件需要硬件操作,速度较慢,会导致性能较低。所以我们考虑将图片缓存起来,需要的时候直接从内存中读取。但是,由于图片占用内存空间比较大,缓存很多图片需要很多的内存,就可能比较容易发生OutOfMemory异常。这时,我们可以考虑使用软引用技术来避免这个问题发生。

 

使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。

需要注意的是,在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃。

简介2

强引用:
        强引用其实相对而言就非常的简单,也就是我们一般实例化对象后,对对象的一个引用就属于强引用,并且只要这个引用存在,那么GC(垃圾回收器)也就绝对不会去回收当前被引用的对象...如果将这个对象的引用设置为null,那么就代表GC可以对这个对象进行回收了...

软引用:         软引用是我要细说的一个部分,先说一下软引用的一个引用目的,软引用的使用是与【内存】挂钩的一个引用类,主要应用于内存敏感的高速缓存,其实在Android中是经常使用的到的,由于Android的虚拟机是基于寄存器的Dalvik,它的堆大小只有16M,我们都清楚无论是Android应用程序,还是Java引用程序,在实例化对象的时候都是在堆中完成的...因此在Android中这个堆的大小要求确实是很苛刻的,一旦我们读取的资源数据过大,或者是内存里的对象由于声明的周期太长,没有被及时的释放,那么就很有可能造成OOM的发生...我们都知道GC的回收机制在正常的情况下并不是时时刻刻都在工作的,它的工作时间是不定期的,因此如果在GC不工作的期间我们的内存已经爆表,那就必定导致OOM,OOM在Android是经常见到的一种情况,对象的不及时释放,static关键字的使用,线程不可控,还有最常见的就是在读取Bitmap的时候导致这种情况的发生...    那么OOM是一个非常严重的问题,但是如果内存得不到充分的使用,这个其实也是一个潜在的问题,SoftReference(软引用),其实给我的感觉就是为了解决这样的问题,每一个对象通过软引用进行实例化,那么这个对象的数据也就会被保存起来,当需要再次调用这个对象中的数据的时候,只需要通过对象的get()方法就可以获取到该对象所保存的数据信息,其实也会是我们所说的cache,当内存快要不足的时候,GC会迅速的把所有的软引用清除掉,释放内存空间...这样不仅结合了对象的cache,同时还解决了OOM的发生,一举两得...虽然说是一举两得的东西,保证了内存能够安全的被使用,那么相反就要牺牲效率,在每一个软引用对象被实例化的时候,其实还是需要耗费许多的时间的...因此到底如何运用,取决于我们遇到的问题.       在Android中,其实有一些时候还是由于Bitmap的问题导致内存不足的发生,图片相对而言还是比较大的,像素高的图片在读取的情况下是非常耗费内存的,如果读取的图片过多的话,那就更加的显而易见了,图片过多,内存会被吃的非常的紧,因此我们需要在Bitmap中去使用软引用,这样就可以避免OOM的发生...还可以有效的去使用内存...         还是简单的说一下其中的道理,比如说我们读取到了一张图片,这张图片被读取后就以cache的形式保存起来...当我们的应用程序如果还是需要这张图片的资源数据的话,那么直接通过软引用中的get()方法,就可以得到图片中的资源数据,..这样就没必要再次进行读取了,直接从cache中就可以读取得到,如果图片过多,保存的对象也会越来越多,那么当内存将要发生OOM的时候,GC会迅速把所有的软引用,也就是这些对象的引用和内控件迅速释放...防止OOM的发生...
弱引用:         弱引用其实和软引用差不太多,不过是使用WeakReference去实例化一个对象,并且GC是不会根据内存的原因去指定时间去回收对象...弱引用可以在未被GC回收之前的任意时间段,都能获取到这个对象的相关信息...一旦GC将其回收,那么就获取不到这个对象的相关数据了...该回收的时候,GC会自动的将其进行回收,不会根据内存的大小而决定时候去释放这个对象所占用的内存...
        总之软引用的使用是为了解决OOM的状况发生...避免OOM的发生不仅仅只使用SoftReference,还有一些其他的方式,比如说减少static关键字的使用,在app中尽量去使用Application中的Context对象...避免线程不可控的情况发生,都是避免OOM的发生的方式...使用SoftReference更多的是在app中加载Bitmap而发生OOM...

来自为知笔记(Wiz)

时间: 2024-08-07 01:29:33

软引用 弱引用 图片的加载与缓存的相关文章

Android新浪微博客户端(七)——ListView中的图片异步加载、缓存

原文出自:方杰|http://fangjie.sinaapp.com/?p=193转载请注明出处 最终效果演示:http://fangjie.sinaapp.com/?page_id=54该项目代码已经放到github:https://github.com/JayFang1993/SinaWeibo 一.ListView的图片异步加载 我们都知道对每一个Weibo Item都有用户头像,而且每一条微博还可能带有图片.如果在加载列表的同时加载图片,这样有几个缺点,第一很费事,界面卡住,用户体验很不

Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)

这个图片异步加载并缓存的类已经被很多开发者所使用,是最常用的几个开源库之一,主流的应用,随便反编译几个火的项目,都可以见到它的身影. 可是有的人并不知道如何去使用这库如何进行配置,网上查到的信息对于刚接触的人来说可能太少了,下面我就把我使用过程中所知道的写了下来,希望可以帮助自己和别人更深入了解这个库的使用和配置. GITHUB上的下载路径为:https://github.com/nostra13/Android-Universal-Image-Loader ,下载最新的库文件,并且导入到项目的

【转】Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)

Android-Universal-Image-Loader 原文地址:http://blog.csdn.net/vipzjyno1/article/details/23206387 这个图片异步加载并缓存的类已经被很多开发者所使用,是最常用的几个开源库之一,主流的应用,随便反编译几个火的项目,都可以见到它的身影. 可是有的人并不知道如何去使用这库如何进行配置,网上查到的信息对于刚接触的人来说可能太少了,下面我就把我使用过程中所知道的写了下来,希望可以帮助自己和别人更深入了解这个库的使用和配置.

安卓universal-image-loader图片异步加载的解析

这个图片异步加载并缓存的类已经被很多开发者所使用,是最常用的几个开源库之一,主流的应用,随便反编译几个火的项目,都可以见到它的身影. 可是有的人并不知道如何去使用这库如何进行配置,网上查到的信息对于刚接触的人来说可能太少了,下面我就把我使用过程中所知道的写了下来,希望可以帮助自己和别人更深入了解这个库的使用和配置. GITHUB上的下载路径为:https://github.com/nostra13/Android-Universal-Image-Loader ,下载最新的库文件,并且导入到项目的

Android-Universal-Image-Loader 图片异步加载类库的使用

在博客中看到一篇利用组件进行图片异步加载的文章在此作记录 原文:http://blog.csdn.net/vipzjyno1/article/details/23206387 这个图片异步加载并缓存的类已经被很多开发者所使用,是最常用的几个开源库之一,主流的应用,随便反编译几个火的项目,都可以见到它的身影. 可是有的人并不知道如何去使用这库如何进行配置,网上查到的信息对于刚接触的人来说可能太少了,下面我就把我使用过程中所知道的写了下来,希望可以帮助自己和别人更深入了解这个库的使用和配置. GIT

Android图片异步加载

原:http://www.cnblogs.com/angeldevil/archive/2012/09/16/2687174.html 相关:https://github.com/nostra13/Android-Universal-Image-Loader 开发Android程序,一般情况下都会有两个操作,图片的异步加载与缓存,而图片的异步加载大都是从网络读取图片(还有生成本地图片缩略图等操作),为了减少网络操作,加快图片加载速度就需要对图片进行缓存,所以网上的好多图片异步加载方法都是与图片的

Android图片异步加载框架Android-Universal-Image-Loader

Android-Universal-Image-Loader是一个图片异步加载,缓存和显示的框架.这个框架已经被很多开发者所使用,是最常用的几个Android开源项目之一,主流的应用,随便反编译几个,都可以见到它的身影.淘宝,天猫,Facebook,京东商城等都用到了这个项目. 该项目的Github地址链接:https://github.com/nostra13/Android-Universal-Image-Loader 运行流程: 每一个图片的加载和显示任务都运行在独立的线程中,除非这个图片

自己动手写android图片异步加载库

尊重他人劳动成果,转载请说明出处:http://blog.csdn.net/bingospunky/article/details/44344085 接触android有半年了,关于图片异步加载,一直只用别人的框架,虽然特别方便,但是始终未见识到图片异步加载的庐山真面目.最近比较悠闲,研究一些高大上的东西.在这篇文章总结一下我对图片异步加载的一些学习心得. 图片加载最重要的无非就是内存和线程.大家都知道关于内存溢出一般的解决方案就是LruCache,在我的这个demo里我只要使用SoftRefe

Android图片异步加载之Android-Universal-Image-Loader

将近一个月没有更新博客了,由于这段时间以来准备毕业论文等各种事务缠身,一直没有时间和精力沉下来继续学习和整理一些东西.最近刚刚恢复到正轨,正好这两天看了下Android上关于图片异步加载的开源项目,就顺便整理记录下来,作为这一个多月来博客的重新开火做饭吧.从今天起我会陆续恢复博客的更新,也希望大家继续支持. 今天要介绍的是Github上一个使用非常广泛的图片异步加载库Android-Universal-Image-Loader,该项目的功能十分强大,可以说是我见过的目前功能最全.性能最优的图片异