【问题标题】:How to use WeakReference in Java and Android development?Java和Android开发中如何使用WeakReference?
【发布时间】:2010-07-14 03:26:57
【问题描述】:

我从事 Java 开发已有 2 年了。

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

【问题讨论】:

标签: java android weak-references


【解决方案1】:

在 Android 中使用 WeakReference 与在普通的旧 Java 中使用 WeakReference 没有任何不同。

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

请务必查看SoftReferencePhantomReference

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

Tom 是对的,complaints 表示由于 WeakHashMap 缓存而导致 Netbeans 性能不佳。

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

【讨论】:

  • 不!不!不! WeakHashMap 用作缓存是致命的。条目可以在创建后立即删除。这在您测试时可能不会发生,但在使用时可能会发生。值得注意的是,NetBeans 可以通过这种方式有效地 100% 停止 CPU。
  • @Tom 我已经更新了我的答案。公平地说,我在技术上是正确的,缓存通常使用WeakHashMap 实现,即使你是正确的,这是一个糟糕的选择;)
  • dbyrne 的出色回答。谢谢你。我认为克里斯没有理由不接受这个答案。
  • @dbyrne 我在 Activity 中使用对象,例如 GridView、ImageView 或 BaseAdapter。在 onDestroy 方法中,当我完成活动时,是否需要使用 Wea​​k/SoftReferences 对这些对象执行某些操作?还是系统自动清理这个对象的内存?
  • 到 java.net 的链接断开
【解决方案2】:

[EDIT2] 我发现了WeakReference 的另一个很好的例子。 Displaying Bitmaps Efficiently 培训指南中的 Processing Bitmaps Off the UI Thread 页面,显示了 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 可能不再存在,例如,如果用户离开 Activity 或在任务完成之前发生配置更改。

编码愉快!


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

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

编码愉快! :)


让我分享一个WeakReference 类的工作示例。这是来自 Android 框架小部件的一个小代码 sn-p,名为 AutoCompleteTextView

简而言之, WeakReference 类用于持有 View 在这个例子中防止memory leak的对象。 p>

我将复制并粘贴 PopupDataSetObserver 类,它是 AutoCompleteTextView 的嵌套类。这真的很简单,cmets 很好地解释了课程。快乐编码! :)

    /**
     * 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 应用程序中的工作示例,我可以在其官方示例应用程序中找到一些示例。但我真的无法理解其中一些的用法。例如ThreadSampleDisplayingBitmaps应用程序在其代码中使用WeakReference,但经过多次测试,我发现get()方法永远不会返回null,因为引用的视图对象在适配器中被回收,而不是然后垃圾收集。

【讨论】:

  • 感谢您提供的精彩示例 - 我真的觉得这应该是该问题的公认答案。干杯!
  • @AckshaeySingh 谢谢! :)
【解决方案3】:

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

如何在 Java 和 Android 中使用 Wea​​kReference

您可以执行以下步骤:

  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
    }

}

AnotherClass 具有对MyClass 的强引用。

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 的替代方法是让另一个类实现接口。这是在Listener/Observer Pattern 中完成的。

实际例子

【讨论】:

  • 令人困惑的解释。 // allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); } 是什么??
  • @likejudo,你是对的。我改进了一些变量和方法的命名。现在怎么样了?
  • 在调用get函数之前,您需要检查doSomething函数中的weakreference对象本身不是null
【解决方案4】:

“规范化”映射是指将相关对象的一个​​实例保存在内存中,而所有其他对象通过指针或某种此类机制查找该特定实例。这就是弱引用可以提供帮助的地方。 简短的回答是 WeakReference 对象可用于创建指向系统中对象的指针,同时仍允许 garbage-collector 回收这些对象。范围。例如,如果我有这样的代码:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-12
    • 1970-01-01
    • 1970-01-01
    • 2011-03-20
    • 1970-01-01
    • 2010-09-22
    • 2011-04-14
    • 2011-09-25
    相关资源
    最近更新 更多