写了那么多篇文章第一次使用MarkDown编辑器。。。
强引用也就是我们一般使用的引用,如若一个对象有强引用,那么即使内存不足的情况出现,强引用对象也不会被轻易的回收
String s = new String();
创建了一个String对象,并用一个变量s存储对这个对象的引用。这就是个强引用。
变量持有的是这个对象的引用。通常,引用是一个对象的存储地址(但不同于c++、c,这个引用不能转换成整数)。而强引用其实就是正在使用的对象
虚引用其实没什么用,和弱引用一样不会介入引用对象的生命周期。它的get方法总是返回null,所以你得不到它引用的对象。它的唯一作用就是跟踪对象合适被回收,但是必须要和引用队列 (ReferenceQueue)联合使用。当垃圾回收机制准备回收一个对象的时候,如果发现这个对象还存在一个虚引用,就会在这个对象被回收之前将这个虚引用加入到引用队列中,如果在引用队列中发现虚引用,那么就说明与之关联的对象将要被回收了。
如果一个对象持有的是弱引用,垃圾回收机制是会去回收这个对象的,但是垃圾回收是优先级很低的线程,所以不一定很快能被回收。这个会在什么情况应用呢?看过android官网图片加载demo的同学们应该就比较能理解,我这里就直接拿过来用,作为弱引用的讲解例子。
class BitmapWorkerTask extends AsyncTask {
private final WeakReference imageViewReference;
private int data = 0;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference(imageView);
}
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
}
// 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);
}
}
}
}
这里就有一个imageview的弱引用,AsyncTask 这个大家都应该不陌生就不再介绍,这里在这个异步加载线程中加载图片,然后显示到imageview上面。而imageview是在ui线程(也就是主线程),我们不能保证用户什么时候会结束这个主线程,而此时已经启动了这个图片异步加载线程,我们不知道它什么时候会加载完,如果线程拥有的是imageview的强引用的话,这个imageview在用户退出的时候就不能被回收,也就会造成内存泄露的情况,在使用handler的时候也经常会遇到这个问题,就是你在activity中启动一个线程,使用handler处理信息,如果activity退出,而线程是不会退出的,还会持有handler对象,也就造成了handler不能被回收,内存泄露就出现了。而弱引用就解决了这个问题,像上面的代码,ui线程结束imageview此时要回收,因为图片加载线程中持有的是imageview的弱引用,他不会影响他所弱引用对象的回收,也就不会造成内存泄露,而此时如果弱引用的对象已经被回收,imageViewReference.get();得到的就是空
软引用在内存优化、速度优化中是经常会被用到的,软引用可用来实现内存敏感的高速缓存。如果一个对象有一个软引用,那么只要内存空间足够,他就不会被回收,我们就可以使用这些对象,相当于回收利用吧。我们知道开辟内存的消耗还是比较大的(不管是事件还是空间上),所以复用就可以做到减少内存的消耗,速度优化等。可以和引用队列联合使用。
这里要再次说下android官网的图片加载demo,还是建议大家去看看。
http://developer.android.com/intl/zh-cn/training/displaying-bitmaps/index.html
这里又要用到这里面的例子程序来讲软引用,但是他的demo中没有结合引用队列使用,下面就结合demo介绍:
首先来说说他的应用场景:异步加载大量的图片,大量图片的话就必定会用到内存缓存以提高速度。使用的是LruCache来缓存图片,也就是结合了最近最久未使用算法的缓存,这个缓存要有大小限制的(要是把全部内存都占了那肯定不行),也就是会有图片从这个缓存中移除,这个时候就用到了软引用,将那些所有被移出缓存的图片(bitmap对象)绑定软引用,加入到一个容器中存储,然后等到需要新的bitmap对象的时候就从这个容器中循环拿可以使用的进行复用(创建新的bitmap内存很耗时,也很费内存,复用大大提高效率)。
下面就是被移除的对象添加软引用的代码
mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {
// Notify the removed entry that is no longer being cached.
@Override
protected void entryRemoved(boolean evicted, String key,
BitmapDrawable oldValue, BitmapDrawable newValue) {
if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
// The removed entry is a recycling drawable, so notify it
// that it has been removed from the memory cache.
((RecyclingBitmapDrawable) oldValue).setIsCached(false);
} else {
// The removed entry is a standard BitmapDrawable.
if (Utils.hasHoneycomb()) {
// We're running on Honeycomb or later, so add the bitmap
// to a SoftReference set for possible use with inBitmap later.
mReusableBitmaps.add
(new SoftReference<Bitmap>(oldValue.getBitmap()));
}
}
}
....
循环找可以复用的bitmap
protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
Bitmap bitmap = null;
if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
synchronized (mReusableBitmaps) {
final Iterator<SoftReference<Bitmap>> iterator
= mReusableBitmaps.iterator();
Bitmap item;
while (iterator.hasNext()) {
item = iterator.next().get();
if (null != item && item.isMutable()) {
// Check to see it the item can be used for inBitmap.
if (canUseForInBitmap(item, options)) {
bitmap = item;
// Remove from reusable set so it can't be used again.
iterator.remove();
break;
}
} else {
// Remove from the set if the reference has been cleared.
iterator.remove();
}
}
}
}
return bitmap;
}
上面我们说了要结合引用队列使用,但是上面的而梨子中其实没有用到。先说说引用队列的用处吧:
String s = new String();
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref=new SoftReference(s, queue);
如上述代码 ,一开始s是强引用,然后创建了一个引用队列,以及结合了引用队列的软引用,此时如果s=null;(上面也讲到了,垃圾回收是一个优先级很低的线程,所以不会那么快回收),但这里假设s被回收了,那么ref就会加入到queue中,此时ref通过get方法得到的结果已经是null了,我们可以利用这个来清除没有用的软引用,如果像上面这样是单个的软引用对象的话就可以不用,但是很多时候我们会有一堆软引用然后都放在容器中,这个时候就需要去清理其中没有用的软引用,因为如果那个存软引用的容器存了一堆没用的,也是很大的消耗。
引用队列的使用以及为什么要使用也说了,为什么上面android官网的加载图片的demo中没有用到引用队列大家结合一下代码看看就知道了,引用队列就一个用处:清理没用的软引用,而上面的demo中我们可以看到他循环读取其中的软引用,判断是否为空(也就是没用了的软引用)等条件,不符合就直接移除了,也就做了清理没用的软引用的工作。
讲完了。。。最后要说,软引用、弱引用真的是比较好用的,一定要学会灵活使用。