如何在 Java 和 Android 开发中使用 WeakReference?

我已经做了两年的 Java 开发人员。

但是我从来没有在我的代码中写过弱引用。如何使用 WeakReference 使我的应用程序更加高效,特别是 Android 应用程序?

103324 次浏览

在 Android 中使用 WeakReference与在普通的 Java 中使用 WeakReference没有什么不同。

当您需要对象的引用时,您应该考虑使用一个引用,但是您不希望该引用保护对象不受垃圾收集器的影响。一个典型的例子是,当内存使用过高时(通常使用 WeakHashMap实现) ,您希望对缓存进行垃圾回收。

一定要检查 SoftReferencePhantomReference以及。

编辑: Tom 提出了一些关于使用 WeakHashMap实现缓存的担忧。这里有一篇文章列出了这些问题: WeakHashMap 不是一个缓存!

Tom 说得对,由于 WeakHashMap缓存,有关 Netbeans 性能差的 投诉报道是正确的。

我仍然认为,使用 WeakHashMap实现缓存,然后将其与使用 SoftReference实现的自己的手卷缓存进行比较,这将是一个很好的学习经验。在现实世界中,您可能不会使用这两种解决方案中的任何一种,因为使用像 Apache JCS这样的第三方库更有意义。

[ EDIT2] 我发现了另一个 WeakReference的好例子。高效显示位图培训指南中的 在 UI 线程外处理位图页,显示了 AsyncTask 中 WeakReference的一种用法。

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;


public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(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 仍然存在,因此您还必须检查 onPostExecute ()中的引用。ImageView 可能不再存在,例如,如果用户从活动导航离开,或者如果在任务完成之前发生配置更改。

编程愉快!


[ EDIT ] 我从 Facebook-android-sdk中找到了一个非常好的 WeakReference例子。工具提示弹出类只是一个简单的小部件类,它在锚点视图上显示工具提示。我截了一张截图。

scrumptious screenshot

这个类非常简单(大约200行) ,值得一看。在该类中,WeakReference类用于保存对锚视图的引用,这非常有意义,因为它使得即使工具提示实例的寿命比锚视图的寿命更长,锚视图也可能被垃圾收集。

编程愉快! :)


让我来分享一个 WeakReference类的工作示例,它是来自 Android 框架小部件 AutoCompleteTextView的一个小代码片段。

总之,在这个例子中,对象 WeakReference 类是用来举行 View < strong > 来阻止 内存泄漏

我将只复制并粘贴 PopupDataSetObserver 类,它是 AutoCompleteTextView的一个嵌套类。它真的很简单,评论很好地解释了这门课。编程愉快!:)

    /**
* Static inner listener that keeps a WeakReference to the actual AutoCompleteTextView.
* <p>
* This way, if adapter has a longer life span than the View, we won't leak the View, instead
* we will just leak a small Observer with 1 field.
*/
private static class PopupDataSetObserver extends DataSetObserver {
private final WeakReference<AutoCompleteTextView> mViewReference;
private PopupDataSetObserver(AutoCompleteTextView view) {
mViewReference = new WeakReference<AutoCompleteTextView>(view);
}
@Override
public void onChanged() {
final AutoCompleteTextView textView = mViewReference.get();
if (textView != null && textView.mAdapter != null) {
// If the popup is not showing already, showing it will cause
// the list of data set observers attached to the adapter to
// change. We can't do it from here, because we are in the middle
// of iterating through the list of observers.
textView.post(updateRunnable);
}
}


private final Runnable updateRunnable = new Runnable() {
@Override
public void run() {
final AutoCompleteTextView textView = mViewReference.get();
if (textView == null) {
return;
}
final ListAdapter adapter = textView.mAdapter;
if (adapter == null) {
return;
}
textView.updateDropDownForFilter(adapter.getCount());
}
};
}

PopupDataSetObserver用于设置适配器。

    public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
if (mObserver == null) {
mObserver = new PopupDataSetObserver(this);
} else if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mObserver);
}
mAdapter = adapter;
if (mAdapter != null) {
//noinspection unchecked
mFilter = ((Filterable) mAdapter).getFilter();
adapter.registerDataSetObserver(mObserver);
} else {
mFilter = null;
}
mPopup.setAdapter(mAdapter);
}

最后一件事。我还想知道 WeakReference在 Android 应用程序中的工作示例,我可以在它的官方示例应用程序中找到一些示例。但是我真的不能理解其中的一些用法。例如,ThreadSample显示位图应用程序在其代码中使用 WeakReference,但是在运行了几个测试之后,我发现 get ()方法从来不返回 null,因为引用的视图对象在适配器中被回收,而不是被垃圾收集。

一个“规范化”映射就是你在内存中保存一个对象的实例,而其他所有的实例通过指针或者类似的机制来查找这个特定的实例。这就是弱引用可以提供帮助的地方。 简短的回答是,弱参考对象可以用来创建指向系统中对象的指针,同时仍然允许这些对象在超出作用域后被 垃圾收集者回收。例如,如果我有这样的代码:

class Registry {
private Set registeredObjects = new HashSet();


public void register(Object object) {
registeredObjects.add( object );
}
}

我注册的任何对象都不会被 GC 回收,因为在 registeredObjects集中存储了对它的引用。另一方面,如果我这样做:

class Registry {
private Set registeredObjects = new HashSet();


public void register(Object object) {
registeredObjects.add( new WeakReference(object) );
}
}

然后,当 GC 想要回收集中的物体时,它就可以这样做了。 您可以将此技术用于缓存、编目等。请参阅下面关于 GC 和缓存的更深入讨论的参考资料。

档号: 垃圾收集器和弱引用

其他一些答案似乎不完整或过于冗长。

如何在 Java 和 Android 中使用 WeakReference

你可按以下步骤办理:

  1. 创建一个 WeakReference变量
  2. 设置弱引用
  3. 用弱引用

密码

MyClassAnotherClass的引用很弱。

public class MyClass {


// 1. Create a WeakReference variable
private WeakReference<AnotherClass> mAnotherClassReference;


// 2. Set the weak reference (nothing special about the method name)
void setWeakReference(AnotherClass anotherClass) {
mAnotherClassReference = new WeakReference<>(anotherClass);
}


// 3. Use the weak reference
void doSomething() {
AnotherClass anotherClass = mAnotherClassReference.get();
if (anotherClass == null) return;
// do something with anotherClass
}


}

AnotherClassMyClass有很强的参考价值。

public class AnotherClass {
    

// strong reference
MyClass mMyClass;
    

// allow MyClass to get a weak reference to this class
void someMethod() {
mMyClass = new MyClass();
mMyClass.setWeakReference(this);
}
}

笔记

  • 需要弱引用的原因是,垃圾收集器可以在不再需要对象时释放它们。如果两个对象保留了对彼此的强引用,那么就不能对它们进行垃圾回收。这是内存泄漏。
  • 如果两个对象需要相互引用,那么对象 A (通常是活动时间较短的对象)应该对对象 B (通常是活动时间较长的对象)有一个弱引用,而对象 B 对 A 有一个强引用。在上面的例子中,MyClass是 A,AnotherClass是 B。
  • 使用 WeakReference的另一种方法是让另一个类实现一个接口。

实际例子