Làm cách nào để sử dụng WeakReference trong phát triển Java và Android?


166

Tôi đã là một nhà phát triển java được 2 năm.

Nhưng tôi chưa bao giờ viết WeakReference trong mã của mình. Làm cách nào để sử dụng WeakReference để làm cho ứng dụng của tôi hiệu quả hơn, đặc biệt là ứng dụng Android?


Có thể có một khác biệt cho Android: stackoverflow.com/questions/299659/ từ
Pacerier

Câu trả lời:


229

Sử dụng một WeakReferencetrong Android không khác gì sử dụng một trong Java cũ đơn giản. Đây là một hướng dẫn tuyệt vời cung cấp một lời giải thích chi tiết: Hiểu các tài liệu tham khảo yếu .

Bạn nên suy nghĩ về việc sử dụng bất cứ khi nào bạn cần một tham chiếu đến một đối tượng, nhưng bạn không muốn tham chiếu đó để bảo vệ đối tượng khỏi trình thu gom rác. Một ví dụ cổ điển là bộ đệm mà bạn muốn là rác được thu thập khi mức sử dụng bộ nhớ quá cao (thường được thực hiện với WeakHashMap).

Hãy chắc chắn để kiểm tra SoftReferencePhantomReferencelà tốt.

EDIT: Tom đã đưa ra một số lo ngại về việc thực hiện bộ đệm với WeakHashMap. Đây là một bài viết đặt ra các vấn đề: WeakHashMap không phải là bộ đệm!

Tom nói đúng rằng đã có những phàn nàn về hiệu suất của Netbeans kém doWeakHashMap bộ nhớ đệm.

Tôi vẫn nghĩ rằng nó sẽ là một kinh nghiệm học tập tốt để thực hiện bộ đệm WeakHashMapvà sau đó so sánh nó với bộ đệm cuộn bằng tay của chính bạn được thực hiện với SoftReference. Trong thế giới thực, có lẽ bạn sẽ không sử dụng một trong hai giải pháp này, vì sẽ hợp lý hơn khi sử dụng thư viện của bên thứ 3 như Apache JCS .


15
Không! Không! Không! WeakHashMapđược sử dụng như một bộ đệm là gây tử vong. Các mục có thể được gỡ bỏ ngay khi chúng được tạo. Điều này có thể sẽ không xảy ra khi bạn đang thử nghiệm, nhưng cũng có thể khi sử dụng. Đáng chú ý, NetBeans có thể được đưa đến điểm dừng CPU 100% hiệu quả bằng cách này.
Tom Hawtin - tackline

3
@Tom Tôi đã cập nhật câu trả lời của tôi. Mặc dù công bằng mà nói, tôi đã đúng về mặt kỹ thuật rằng bộ nhớ cache thường được triển khai WeakHashMapngay cả khi bạn đúng rằng đó là một lựa chọn tồi;)
dbyrne

1
Câu trả lời tuyệt vời của dbyrne. Cảm ơn vì điều đó. Tôi thấy không có lý do gì để chris không chấp nhận câu trả lời này.
san

@dbyrne Tôi đang sử dụng các đối tượng trong Hoạt động của mình như GridView, ImageView hoặc BaseAd CHƯƠNG. Trong phương thức onDestroy, khi tôi kết thúc hoạt động, tôi có cần phải làm gì đó với các đối tượng này bằng Weak / SoftReferences không? Hoặc hệ thống tự động dọn dẹp bộ nhớ của đối tượng này?
beni

4
liên kết bị hỏng đến java.net
Suragch

64

[EDIT2] Tôi tìm thấy một ví dụ hay khác về WeakReference. Xử lý Bitmap Tắt trang Chủ đề giao diện người dùng trong Hiển thị Bitmap Hướng dẫn đào tạo hiệu quả , hiển thị một cách sử dụng WeakReferencetrong AsyncTask.

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);
            }
        }
    }
}

Nó nói rằng,

WeakReference cho ImageView đảm bảo rằng AsyncTask không ngăn ImageView và bất cứ thứ gì nó tham chiếu khỏi rác được thu thập . Không có gì đảm bảo ImageView vẫn ở xung quanh khi tác vụ kết thúc, do đó bạn cũng phải kiểm tra tham chiếu trong onPostExecute (). ImageView có thể không còn tồn tại, ví dụ, nếu người dùng điều hướng khỏi hoạt động hoặc nếu thay đổi cấu hình xảy ra trước khi tác vụ kết thúc.

Chúc mừng mã hóa!


[EDIT] Tôi đã tìm thấy một ví dụ thực sự tốt WeakReferencetừ facebook-android-sdk . Lớp ToolTipPopup không có gì ngoài một lớp widget đơn giản hiển thị tooltip phía trên khung nhìn neo. Tôi chụp ảnh màn hình.

ảnh chụp màn hình hảo hạng

Lớp học thực sự đơn giản (khoảng 200 dòng) và đáng để xem xét. Trong lớp đóWeakReference lớp được sử dụng để giữ tham chiếu đến chế độ xem neo, điều này có ý nghĩa hoàn hảo, bởi vì nó có thể cho chế độ xem neo là rác được thu thập ngay cả khi một đối tượng chú giải công cụ tồn tại lâu hơn chế độ xem neo.

Chúc mừng mã hóa! :)


Hãy để tôi chia sẻ một ví dụ làm việc của WeakReferencelớp. Đó là một đoạn mã nhỏ từ tiện ích khung Android được gọi AutoCompleteTextView.

Nói tóm lại, WeakReference lớp được sử dụng để giữ View đối tượng để ngăn chặn rò rỉ bộ nhớ trong ví dụ này.

Tôi sẽ chỉ sao chép và dán lớp PopupDataSetObserver, đây là một lớp lồng nhau AutoCompleteTextView. Nó thực sự đơn giản và các ý kiến ​​giải thích tốt về lớp học. Chúc mừng mã hóa! :)

    /**
     * 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được sử dụng trong cài đặt bộ điều hợp.

    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);
}

Một điều cuối cùng. Tôi cũng muốn biết ví dụ hoạt động WeakReferencetrong ứng dụng Android và tôi có thể tìm thấy một số mẫu trong các ứng dụng mẫu chính thức của nó. Nhưng tôi thực sự không thể hiểu một số cách sử dụng chúng. Ví dụ, các ứng dụng ThreadSampleDisplayingBitmaps sử dụng WeakReferencemã của nó, nhưng sau khi chạy một số thử nghiệm, tôi phát hiện ra rằng phương thức get () không bao giờ trả về null, vì đối tượng khung nhìn được tham chiếu được tái chế trong bộ điều hợp, thay vào đó là rác được thu thập.


1
Cảm ơn các ví dụ tuyệt vời - tôi thực sự cảm thấy rằng đây nên là câu trả lời được chấp nhận cho câu hỏi. Chúc mừng!
Ackshaey Singh

@AckshaeySingh Cảm ơn! :)
김준호

16

Một số câu trả lời khác có vẻ không đầy đủ hoặc quá dài. Đây là một câu trả lời chung.

Cách sử dụng WeakReference trong Java và Android

Bạn có thể làm các bước sau:

  1. Tạo một WeakReference biến
  2. Đặt tham chiếu yếu
  3. Sử dụng tài liệu tham khảo yếu

MyClasscó một tài liệu tham khảo yếu 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
    }

}

AnotherClasscó một tài liệu tham khảo mạnh mẽ để 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);
    }
}

Ghi chú

  • Lý do bạn cần một tài liệu tham khảo yếu là để Bộ sưu tập rác có thể loại bỏ các đối tượng khi chúng không còn cần thiết. Nếu hai đối tượng giữ một tham chiếu mạnh với nhau, thì chúng không thể được thu gom rác. Đây là một rò rỉ bộ nhớ.
  • Nếu hai đối tượng cần tham chiếu lẫn nhau, đối tượng A (nói chung là đối tượng sống ngắn hơn) nên có tham chiếu yếu đến đối tượng B (nói chung là đối tượng sống lâu hơn), trong khi B có tham chiếu mạnh đến A. Trong ví dụ trên, MyClasslà A vàAnotherClass được B.
  • Một cách khác để sử dụng a WeakReferencelà để một lớp khác thực hiện một giao diện. Điều này được thực hiện trong Mẫu Listener / Observer .

Ví dụ thực tế


giải thích khó hiểu. là gì // allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); }??
likejudo

@likejudo, bạn nói đúng. Tôi đã cải thiện một số tên và phương pháp đặt tên. Làm thế nào bây giờ?
Suragch

bạn cần kiểm tra weakreferencechính đối tượng trong doSomethingchức năng để không bị nulltrước khi gọi gethàm.
Behrouz.M

7

Ánh xạ "chuẩn hóa" là nơi bạn giữ một thể hiện của đối tượng trong bộ nhớ và tất cả các đối tượng khác tra cứu trường hợp cụ thể đó thông qua con trỏ hoặc cơ chế somesuch. Đây là nơi mà các tài liệu tham khảo yếu có thể giúp đỡ. Câu trả lời ngắn gọn là các đối tượng WeakReference có thể được sử dụng để tạo con trỏ tới các đối tượng trong hệ thống của bạn trong khi vẫn cho phép các đối tượng đó được thu thập bởi bộ thu gom rác khi chúng vượt ra khỏi phạm vi. Ví dụ: nếu tôi có mã như thế này:

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

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

Bất kỳ đối tượng nào tôi đăng ký sẽ không bao giờ được thu hồi bởi GC vì có một tham chiếu đến nó được lưu trữ trong tập hợp registeredObjects. Mặt khác, nếu tôi làm điều này:

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

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

Sau đó, khi GC muốn lấy lại các đối tượng trong Set, nó sẽ có thể làm như vậy. Bạn có thể sử dụng kỹ thuật này để lưu vào bộ nhớ cache, lập danh mục, v.v ... Xem bên dưới để tham khảo các thảo luận chuyên sâu hơn về GC và bộ nhớ đệm.

Tham chiếu: Người thu gom rác và WeakReference

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.