[英]How to use WeakReference in Java and Android development?
我已經做了 2 年的 Java 開發人員。
但是我從來沒有在我的代碼中寫過 WeakReference。 如何使用 WeakReference 使我的應用程序更高效,尤其是 Android 應用程序?
在 Android 中使用WeakReference
與在普通的舊 Java 中使用WeakReference
沒有什么不同。
當您需要對對象的引用時,您應該考慮使用一個,但您不希望該引用保護對象免受垃圾收集器的影響。 一個經典的例子是當內存使用率過高時你希望被垃圾收集的緩存(通常用WeakHashMap
實現)。
請務必查看SoftReference
和PhantomReference
。
編輯: Tom 對使用WeakHashMap
實現緩存提出了一些擔憂。 這是一篇闡述問題的文章: WeakHashMap 不是緩存!
Tom 是對的,有人抱怨由於WeakHashMap
緩存導致 Netbeans 性能不佳。
我仍然認為使用WeakHashMap
實現緩存然后將其與您自己使用SoftReference
實現的手動緩存進行比較會是一個很好的學習體驗。 在現實世界中,您可能不會使用這些解決方案中的任何一個,因為使用Apache JCS 之類的 3rd 方庫更有意義。
[EDIT2]我發現了另一個WeakReference
好例子。 在Displaying Bitmaps Efficiently培訓指南中從 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 的 WeakReference 確保 AsyncTask 不會阻止 ImageView 及其引用的任何內容被垃圾收集。 無法保證任務完成時 ImageView 仍然存在,因此您還必須檢查 onPostExecute() 中的引用。 ImageView 可能不再存在,例如,如果用戶導航離開活動或者在任務完成之前發生配置更改。
快樂編碼!
[編輯]我從facebook-android-sdk找到了一個非常好的WeakReference
示例。 ToolTipPopup類只不過是一個簡單的小部件類,它在錨視圖上方顯示工具提示。 我截取了屏幕截圖。
這個類真的很簡單(大約 200 行),值得一看。 在該類中, WeakReference
類用於保存對錨視圖的引用,這是非常有意義的,因為即使工具提示實例的壽命長於其錨視圖,錨視圖也可以被垃圾收集。
快樂編碼! :)
讓我分享一個WeakReference
類的工作示例。 這是來自 Android 框架小部件的一小段代碼片段,名為AutoCompleteTextView
。
簡而言之, WeakReference
類用於保存View
對象,以防止本示例中的內存泄漏。
我將只復制並粘貼 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
工作示例,我可以在其官方示例應用程序中找到一些示例。 但我真的無法理解其中的一些用法。 例如, ThreadSample和DisplayingBitmaps應用程序在其代碼中使用WeakReference
,但在運行幾次測試后,我發現 get() 方法從不返回null
,因為引用的視圖對象在適配器中被回收,而不是垃圾收集。
其他一些答案似乎不完整或過長。 這是一個通用的答案。
您可以執行以下步驟:
WeakReference
變量 MyClass
對AnotherClass
有一個弱引用。
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);
}
}
MyClass
是A 和AnotherClass
是 B。WeakReference
的替代方法是讓另一個類實現一個接口。 這是在Listener/Observer Pattern 中完成的。“規范化”映射是您將所討論對象的一個實例保留在內存中,而所有其他實例通過指針或某種此類機制查找該特定實例。 這是弱引用可以提供幫助的地方。 簡短的回答是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 和緩存的更深入討論的參考,請參見下文。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.