簡體   English   中英

Java和Android開發中如何使用WeakReference?

[英]How to use WeakReference in Java and Android development?

我已經做了 2 年的 Java 開發人員。

但是我從來沒有在我的代碼中寫過 WeakReference。 如何使用 Wea​​kReference 使我的應用程序更高效,尤其是 Android 應用程序?

在 Android 中使用WeakReference與在普通的舊 Java 中使用WeakReference沒有什么不同。

當您需要對對象的引用時,您應該考慮使用一個,但您不希望該引用保護對象免受垃圾收集器的影響。 一個經典的例子是當內存使用率過高時你希望被垃圾收集的緩存(通常用WeakHashMap實現)。

請務必查看SoftReferencePhantomReference

編輯: 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工作示例,我可以在其官方示例應用程序中找到一些示例。 但我真的無法理解其中的一些用法。 例如, ThreadSampleDisplayingBitmaps應用程序在其代碼中使用WeakReference ,但在運行幾次測試后,我發現 get() 方法從不返回null ,因為引用的視圖對象在適配器中被回收,而不是垃圾收集。

其他一些答案似乎不完整或過長。 這是一個通用的答案。

如何在 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
    }

}

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的替代方法是讓另一個類實現一個接口。 這是在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 和緩存的更深入討論的參考,請參見下文。

參考:垃圾收集器和 WeakReference

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM