对象的引用类型:
⑴强引用(StrongReference)
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
⑵软引用(SoftReference)
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
⑶弱引用(WeakReference)
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
⑷虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
弱引用(WeakReference)一般用来防止内存泄漏,要保证内存被VM回收。
弱引用对象,它们并不禁止其指示对象变得可终结,并被终结,然后被回收。弱引用最常用于实现规范化的映射。
假定垃圾回收器确定在某一时间点上某个对象是弱可到达对象。这时,它将自动清除针对此对象的所有弱引用,以及通过强引用链和软引用,可以从其到达该对象的针对任何其他弱可到达对象的所有弱引用。同时它将声明所有以前的弱可到达对象为可终结的。在同一时间或晚些时候,它将那些已经向引用队列注册的新清除的弱引用加入队列。
软引用(SoftReference),好像多用作来实现cache机制。
软引用对象,在响应内存需要时,由垃圾回收器决定是否清除此对象,软引用对象最常用于实现内存敏感的缓存;
假定垃圾回收器确定在某一时间点某个对象是软可到达对象。这时,它可以选择自动清除针对该对象的所有软引用,以及通过强引用链,从其可以到达该对象的针对任何其他软可到达对象的所有软引用。在同一时间或晚些时候,它会将那些已经向引用队列注册的新清除的软引用加入队列。
软可到达对象的所有软引用都要保证在虚拟机抛出OutOfMemoryError之前已经被清除。否则,清除软引用的时间或者清除不同对象的一组此类引用的顺序将不受任何约束。然而,虚拟机实现不鼓励清除最近访问或使用过的软引用。
此类的直接实例可用于实现简单缓存;该类或其派生的子类还可用于更大型的数据结构,以实现更复杂的缓存。只要软引用的指示对象是强可到达对象,即正在实际使用的对象,就不会清除软引用。例如,通过保持最近使用的项的强指示对象,并由垃圾回收器决定是否放弃剩余的项,复杂的缓存可以防止放弃最近使用的项。
软引用的主要特点是具有较强的引用功能。只有当内存不够的时候才回收这类内存,因此在内存足够的时候,他们通常不被回收。另外,这些引用对象还能保证在Java抛出OutOfMemory异常之前,被设置为null。他可以用于实现一些常用资源的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory异常。
⑴强引用(StrongReference)
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
⑵软引用(SoftReference)
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
⑶弱引用(WeakReference)
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
⑷虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
弱引用(WeakReference)一般用来防止内存泄漏,要保证内存被VM回收。
弱引用对象,它们并不禁止其指示对象变得可终结,并被终结,然后被回收。弱引用最常用于实现规范化的映射。
假定垃圾回收器确定在某一时间点上某个对象是弱可到达对象。这时,它将自动清除针对此对象的所有弱引用,以及通过强引用链和软引用,可以从其到达该对象的针对任何其他弱可到达对象的所有弱引用。同时它将声明所有以前的弱可到达对象为可终结的。在同一时间或晚些时候,它将那些已经向引用队列注册的新清除的弱引用加入队列。
软引用(SoftReference),好像多用作来实现cache机制。
软引用对象,在响应内存需要时,由垃圾回收器决定是否清除此对象,软引用对象最常用于实现内存敏感的缓存;
假定垃圾回收器确定在某一时间点某个对象是软可到达对象。这时,它可以选择自动清除针对该对象的所有软引用,以及通过强引用链,从其可以到达该对象的针对任何其他软可到达对象的所有软引用。在同一时间或晚些时候,它会将那些已经向引用队列注册的新清除的软引用加入队列。
软可到达对象的所有软引用都要保证在虚拟机抛出OutOfMemoryError之前已经被清除。否则,清除软引用的时间或者清除不同对象的一组此类引用的顺序将不受任何约束。然而,虚拟机实现不鼓励清除最近访问或使用过的软引用。
此类的直接实例可用于实现简单缓存;该类或其派生的子类还可用于更大型的数据结构,以实现更复杂的缓存。只要软引用的指示对象是强可到达对象,即正在实际使用的对象,就不会清除软引用。例如,通过保持最近使用的项的强指示对象,并由垃圾回收器决定是否放弃剩余的项,复杂的缓存可以防止放弃最近使用的项。
软引用的主要特点是具有较强的引用功能。只有当内存不够的时候才回收这类内存,因此在内存足够的时候,他们通常不被回收。另外,这些引用对象还能保证在Java抛出OutOfMemory异常之前,被设置为null。他可以用于实现一些常用资源的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory异常。
软引用技术的引进使Java应用可以更好的管理内存,稳定系统,防止系统内存溢出,避免系统崩溃。因此在处理一些占用内存大而且声明周期较长,但使用并不频繁的对象时应尽量应用该技术。但事物总带有两面性的,有利也有弊,在某些时候对软引用的使用会降低应用的运行效率与性能,例如:应用软引用的对象的初始化过程较为耗时,或者对象的状态在程序的运行过程中发生了变化,都会给重新创建对象与初始化对象带来不同程度的麻烦,有些时候我们要权衡利弊择时应用。
软应用的实现:
Object o = new Object();
//将它设置为soft引用类型
SoftReference sr = new SoftReference(o);
//使用
if (sr != null) {
o = (Object) sr.get();
} else {
// GC由于低内存,已释放o,因此需要重新装载
o = new Object();
sr = new SoftReference(o);
} Android 可以使用软引用实现减少使用Bitmap造成的OOM,下面是我写自定义控件的时候写的使用软引用用于加载Bitmap:
private static class BitmapSoftReferenceHolder {
private Context mContext;
private int mResId;
private SoftReference<Bitmap> mBitmapRef;
private BitmapSoftReferenceHolder(Context context, int resId) {
mContext = context;
mResId = resId;
}
public static BitmapSoftReferenceHolder create(Context context, int resId) {
return new BitmapSoftReferenceHolder(context, resId);
};
private Bitmap getResBitmap() {
return BitmapFactory.decodeResource(mContext.getResources(), mResId);
}
public Bitmap getBitmap() {
if (null == mBitmapRef || null == mBitmapRef.get()) {
mBitmapRef = new SoftReference<Bitmap>(getResBitmap());
}
return mBitmapRef.get();
}
}
在BaseAdapter里面使用软应用加载Bitmap,来自MiCode:
import java.lang.ref.SoftReference;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import net.micode.fileexplorer.FileCategoryHelper.FileCategory;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.Handler.Callback;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.provider.MediaStore.Files.FileColumns;
import android.util.Log;
import android.widget.ImageView;
/**
* Asynchronously loads file icons and thumbnail, mostly single-threaded.
*/
public class FileIconLoader implements Callback {
private static final String LOADER_THREAD_NAME = "FileIconLoader";
/**
* Type of message sent by the UI thread to itself to indicate that some
* photos need to be loaded.
*/
private static final int MESSAGE_REQUEST_LOADING = 1;
/**
* Type of message sent by the loader thread to indicate that some photos
* have been loaded.
*/
private static final int MESSAGE_ICON_LOADED = 2;
private static abstract class ImageHolder {
public static final int NEEDED = 0;
public static final int LOADING = 1;
public static final int LOADED = 2;
int state;
public static ImageHolder create(FileCategory cate) {
switch (cate) {
case Apk:
return new DrawableHolder();
case Picture:
case Video:
return new BitmapHolder();
}
return null;
};
public abstract boolean setImageView(ImageView v);
public abstract boolean isNull();
public abstract void setImage(Object image);
}
private static class BitmapHolder extends ImageHolder {
SoftReference<Bitmap> bitmapRef;
@Override
public boolean setImageView(ImageView v) {
if (bitmapRef.get() == null)
return false;
v.setImageBitmap(bitmapRef.get());
return true;
}
@Override
public boolean isNull() {
return bitmapRef == null;
}
@Override
public void setImage(Object image) {
bitmapRef = image == null ? null : new SoftReference<Bitmap>((Bitmap) image);
}
}
private static class DrawableHolder extends ImageHolder {
SoftReference<Drawable> drawableRef;
@Override
public boolean setImageView(ImageView v) {
if (drawableRef.get() == null)
return false;
v.setImageDrawable(drawableRef.get());
return true;
}
@Override
public boolean isNull() {
return drawableRef == null;
}
@Override
public void setImage(Object image) {
drawableRef = image == null ? null : new SoftReference<Drawable>((Drawable) image);
}
}
/**
* A soft cache for image thumbnails. the key is file path
*/
private final static ConcurrentHashMap<String, ImageHolder> mImageCache = new ConcurrentHashMap<String, ImageHolder>();
/**
* A map from ImageView to the corresponding photo ID. Please note that this
* photo ID may change before the photo loading request is started.
*/
private final ConcurrentHashMap<ImageView, FileId> mPendingRequests = new ConcurrentHashMap<ImageView, FileId>();
/**
* Handler for messages sent to the UI thread.
*/
private final Handler mMainThreadHandler = new Handler(this);
/**
* Thread responsible for loading photos from the database. Created upon the
* first request.
*/
private LoaderThread mLoaderThread;
/**
* A gate to make sure we only send one instance of MESSAGE_PHOTOS_NEEDED at
* a time.
*/
private boolean mLoadingRequested;
/**
* Flag indicating if the image loading is paused.
*/
private boolean mPaused;
private final Context mContext;
private IconLoadFinishListener iconLoadListener;
/**
* Constructor.
*
* @param context content context
*/
public FileIconLoader(Context context, IconLoadFinishListener l) {
mContext = context;
iconLoadListener = l;
}
public static class FileId {
public String mPath;
public long mId; // database id
public FileCategory mCategory;
public FileId(String path, long id, FileCategory cate) {
mPath = path;
mId = id;
mCategory = cate;
}
}
public abstract static interface IconLoadFinishListener {
void onIconLoadFinished(ImageView view);
}
/**
* Load photo into the supplied image view. If the photo is already cached,
* it is displayed immediately. Otherwise a request is sent to load the
* photo from the database.
*
* @param id, database id
*/
public boolean loadIcon(ImageView view, String path, long id, FileCategory cate) {
boolean loaded = loadCachedIcon(view, path, cate);
if (loaded) {
mPendingRequests.remove(view);
} else {
FileId p = new FileId(path, id, cate);
mPendingRequests.put(view, p);
if (!mPaused) {
// Send a request to start loading photos
requestLoading();
}
}
return loaded;
}
public void cancelRequest(ImageView view) {
mPendingRequests.remove(view);
}
/**
* Checks if the photo is present in cache. If so, sets the photo on the
* view, otherwise sets the state of the photo to
* {@link BitmapHolder#NEEDED}
*/
private boolean loadCachedIcon(ImageView view, String path, FileCategory cate) {
ImageHolder holder = mImageCache.get(path);
if (holder == null) {
holder = ImageHolder.create(cate);
if (holder == null)
return false;
mImageCache.put(path, holder);
} else if (holder.state == ImageHolder.LOADED) {
if (holder.isNull()) {
return true;
}
// failing to set imageview means that the soft reference was
// released by the GC, we need to reload the photo.
if (holder.setImageView(view)) {
return true;
}
}
holder.state = ImageHolder.NEEDED;
return false;
}
public long getDbId(String path, boolean isVideo) {
String volumeName = "external";
Uri uri = isVideo ? Video.Media.getContentUri(volumeName) : Images.Media.getContentUri(volumeName);
String selection = FileColumns.DATA + "=?";
;
String[] selectionArgs = new String[] {
path
};
String[] columns = new String[] {
FileColumns._ID, FileColumns.DATA
};
Cursor c = mContext.getContentResolver()
.query(uri, columns, selection, selectionArgs, null);
if (c == null) {
return 0;
}
long id = 0;
if (c.moveToNext()) {
id = c.getLong(0);
}
c.close();
return id;
}
/**
* Stops loading images, kills the image loader thread and clears all
* caches.
*/
public void stop() {
pause();
if (mLoaderThread != null) {
mLoaderThread.quit();
mLoaderThread = null;
}
clear();
}
public void clear() {
mPendingRequests.clear();
mImageCache.clear();
}
/**
* Temporarily stops loading
*/
public void pause() {
mPaused = true;
}
/**
* Resumes loading
*/
public void resume() {
mPaused = false;
if (!mPendingRequests.isEmpty()) {
requestLoading();
}
}
/**
* Sends a message to this thread itself to start loading images. If the
* current view contains multiple image views, all of those image views will
* get a chance to request their respective photos before any of those
* requests are executed. This allows us to load images in bulk.
*/
private void requestLoading() {
if (!mLoadingRequested) {
mLoadingRequested = true;
mMainThreadHandler.sendEmptyMessage(MESSAGE_REQUEST_LOADING);
}
}
/**
* Processes requests on the main thread.
*/
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_REQUEST_LOADING: {
mLoadingRequested = false;
if (!mPaused) {
if (mLoaderThread == null) {
mLoaderThread = new LoaderThread();
mLoaderThread.start();
}
mLoaderThread.requestLoading();
}
return true;
}
case MESSAGE_ICON_LOADED: {
if (!mPaused) {
processLoadedIcons();
}
return true;
}
}
return false;
}
/**
* Goes over pending loading requests and displays loaded photos. If some of
* the photos still haven't been loaded, sends another request for image
* loading.
*/
private void processLoadedIcons() {
Iterator<ImageView> iterator = mPendingRequests.keySet().iterator();
while (iterator.hasNext()) {
ImageView view = iterator.next();
FileId fileId = mPendingRequests.get(view);
boolean loaded = loadCachedIcon(view, fileId.mPath, fileId.mCategory);
if (loaded) {
iterator.remove();
iconLoadListener.onIconLoadFinished(view);
}
}
if (!mPendingRequests.isEmpty()) {
requestLoading();
}
}
/**
* The thread that performs loading of photos from the database.
*/
private class LoaderThread extends HandlerThread implements Callback {
private Handler mLoaderThreadHandler;
public LoaderThread() {
super(LOADER_THREAD_NAME);
}
/**
* Sends a message to this thread to load requested photos.
*/
public void requestLoading() {
if (mLoaderThreadHandler == null) {
mLoaderThreadHandler = new Handler(getLooper(), this);
}
mLoaderThreadHandler.sendEmptyMessage(0);
}
/**
* Receives the above message, loads photos and then sends a message to
* the main thread to process them.
*/
public boolean handleMessage(Message msg) {
Iterator<FileId> iterator = mPendingRequests.values().iterator();
while (iterator.hasNext()) {
FileId id = iterator.next();
ImageHolder holder = mImageCache.get(id.mPath);
if (holder != null && holder.state == ImageHolder.NEEDED) {
// Assuming atomic behavior
holder.state = ImageHolder.LOADING;
switch (id.mCategory) {
case Apk:
Drawable icon = Util.getApkIcon(mContext, id.mPath);
holder.setImage(icon);
break;
case Picture:
case Video:
boolean isVideo = id.mCategory == FileCategory.Video;
if (id.mId == 0)
id.mId = getDbId(id.mPath, isVideo);
if (id.mId == 0) {
Log.e("FileIconLoader", "Fail to get dababase id for:" + id.mPath);
}
holder.setImage(isVideo ? getVideoThumbnail(id.mId) : getImageThumbnail(id.mId));
break;
}
holder.state = BitmapHolder.LOADED;
mImageCache.put(id.mPath, holder);
}
}
mMainThreadHandler.sendEmptyMessage(MESSAGE_ICON_LOADED);
return true;
}
private static final int MICRO_KIND = 3;
private Bitmap getImageThumbnail(long id) {
return Images.Thumbnails.getThumbnail(mContext.getContentResolver(), id, MICRO_KIND, null);
}
private Bitmap getVideoThumbnail(long id) {
return Video.Thumbnails.getThumbnail(mContext.getContentResolver(), id, MICRO_KIND, null);
}
}
}

被折叠的 条评论
为什么被折叠?



