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

时间:2010-07-14 03:26:57

标签: java android weak-references

我是一名Java开发人员已有2年了。

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

4 个答案:

答案 0 :(得分:215)

在Android中使用WeakReference与使用普通旧Java中的WeakHashMap没什么不同。这是一个很好的指南,它给出了详细的解释:Understanding Weak References

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

请务必查看PhantomReferenceWeakHashMap

编辑: Tom对使用WeakHashMap实施缓存提出了一些担忧。这是一篇阐述问题的文章:WeakHashMap is not a cache!

Tom是正确的,因为WeakHashMap缓存导致Netbeans性能不佳complaints

我仍然认为使用SoftReference实现缓存是一种很好的学习体验,然后将其与您使用{{1}}实现的手动缓存进行比较。在现实世界中,您可能不会使用这些解决方案中的任何一个,因为使用第三方库(如Apache JCS)更有意义。

答案 1 :(得分:59)

[EDIT2] 我找到了另一个WeakReference的好例子。 Processing Bitmaps Off the UI Thread培训指南中的Displaying Bitmaps Efficiently页面显示了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的WeakReference确保AsyncTask不会阻止ImageView及其引用的任何内容被垃圾回收。当任务完成时,无法保证ImageView仍然存在,因此您还必须检查onPostExecute()中的引用。 ImageView可能不再存在,例如,用户导航远离活动或者在任务完成之前发生配置更改。

快乐的编码!


[编辑] 我从facebook-android-sdk找到了一个非常好的WeakReference示例。 ToolTipPopup类只是一个简单的窗口小部件类,它在锚点视图上方显示工具提示。我抓了一个截图。

scrumptious screenshot

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

快乐的编码! :)


让我分享WeakReference课程的一个实例。这是Android框架小部件中名为AutoCompleteTextView的一小段代码片段。

简而言之, WeakReference 类用于保存 View 对象,以阻止此示例中的memory leak

我只是复制并粘贴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);
}

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

答案 2 :(得分:13)

其他一些答案似乎不完整或过长。这是一般性答案。

如何在Java和Android中使用WeakReference

您可以执行以下步骤:

  1. 创建<option>变量
  2. 设置弱参考
  3. 使用弱参考
  4. 代码

    WeakReferenceMyClass的引用不足。

    AnotherClass

    public class MyClass { // 1. Create a WeakReference variable private WeakReference<AnotherClass> mAnotherClassReference; // 2. Set the weak reference void someMethod(AnotherClass object) { mAnotherClassReference = new WeakReference<>(object); } // 3. Use the weak reference void anotherMethod() { AnotherClass object = mAnotherClassReference.get(); if (object == null) return; // do something with the object } } 强烈引用AnotherClass

    MyClass

    备注

    • 您需要弱引用的原因是垃圾收集器可以在不再需要时处置这些对象。如果两个对象保持对彼此的强引用,则它们不能被垃圾收集。这是内存泄漏。
    • 如果两个对象需要相互引用,则对象A(通常是较短的对象)应该对对象B(通常是较长寿的对象)具有弱引用,而B对A具有强引用。在上面的示例中,public class AnotherClass { // strong reference MyClass mMyClass; // allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); } } 为A,MyClass为B。
    • 使用AnotherClass的另一种方法是让另一个类实现一个接口。这是在Listener/Observer Pattern中完成的。

    实际例子

答案 3 :(得分:6)

A&#34;规范化&#34;映射是将内存中的一个对象实例保存在内存中,而其他所有实例都通过指针或某种机制查找该特定实例。这是弱参考可以帮助的地方。 简短的回答是, WeakReference 对象可以用来创建指向系统中对象的指针,同时仍然允许垃圾收集器在它们传出后回收这些对象。范围。例如,如果我有这样的代码:

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想要回收Set中的对象时,它将能够这样做。 您可以使用此技术进行缓存,编目等。请参阅下文,以获取有关GC和缓存的更深入讨论的参考。

参考:Garbage collector and WeakReference