Як використовувати WeakReference в розробці Java та Android?


166

Я розробник Java протягом 2 років.

Але я ніколи не писав у своєму коді WeakReference. Як використовувати WeakReference, щоб зробити моє додаток більш ефективним, особливо додаток Android?


Там може бути різниця для Android: stackoverflow.com/questions/299659 / ...
Pacerier

Відповіді:


229

Використання WeakReferenceв Android не відрізняється від використання в звичайній старій Java. Ось чудовий посібник, який дає детальне пояснення: Розуміння слабких посилань .

Вам слід подумати про використання його кожного разу, коли вам потрібна посилання на об'єкт, але ви не хочете, щоб ця посилання захищала об'єкт від сміттєзбірника. Класичний приклад - кеш, який потрібно збирати сміттям, коли використання пам'яті стає надто високим (часто реалізується за допомогою WeakHashMap).

Не забудьте перевірити SoftReferenceі PhantomReference.

EDIT: Том висловив деякі занепокоєння щодо впровадження кешу WeakHashMap. Ось стаття, що викладає проблеми: WeakHashMap - це не кеш!

Том прав, що були скарги на погану продуктивність Netbeans через WeakHashMapкешування.

Я все ще думаю, що було б добре вивчити кеш, WeakHashMapа потім порівняти його з власним кешованим кешем, реалізованим із SoftReference. У реальному світі ви, мабуть, не скористалися б жодним із цих рішень, оскільки має сенс використовувати сторонню бібліотеку, як Apache JCS .


15
Немає! Немає! Немає! WeakHashMapвикористовується як кеш - смертельно. Записи можна видалити, як тільки вони створені. Це, мабуть, не відбудеться під час тестування, але цілком може бути під час використання. Зверніть увагу, NetBeans може бути доведений до ефективної стовідсоткової зупинки процесором цим.
Том Хотін - тайклін

3
@Tom я оновив свою відповідь. Якщо бути справедливим, я був технічно правильним, що кеші часто реалізовуються, WeakHashMapнавіть якщо ви правильні, що це поганий вибір;)
dbyrne

1
Відмінна відповідь від dbyrne. Дякую за це Я не бачу причин, щоб Кріс не прийняв цю відповідь.
Сан

@dbyrne Я використовую такі об’єкти в своїй діяльності, як GridView, ImageView або BaseAdapter. У методі onDestroy, коли я закінчую діяльність, чи потрібно щось робити з цими об'єктами за допомогою слабких / SoftReferences? Або система автоматично очищає цю пам'ять цього об’єкта?
beni

4
перерване посилання на java.net
Сурагч

64

[EDIT2] Я знайшов ще один хороший приклад WeakReference. Обробка растрових зображень Off Нитка UI сторінці в Відображення растрових зображень Ефективне навчальний посібник, демонструє одне використання WeakReferenceв 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);
            }
        }
    }
}

Він говорить,

Слабка реакція на ImageView гарантує, що AsyncTask не заважає ImageView і будь-чому, на що він посилається, збирати сміття . Немає гарантії, що ImageView все ще існує, коли завдання закінчується, тому ви також повинні перевірити посилання в onPostExecute (). ImageView може більше не існувати, якщо, наприклад, користувач відходить від діяльності або якщо зміна конфігурації відбудеться до завершення завдання.

Щасливого кодування!


[EDIT] Я знайшов дійсно хороший приклад WeakReferenceз facebook-android-sdk . Клас ToolTipPopup - це не що інше, як простий клас віджетів, який показує підказку над поданням на якір. Я зробив знімок екрана.

вибагливий скріншот

Клас дійсно простий (близько 200 рядків) і гідний для ознайомлення. У цьому класі WeakReferenceклас використовується для посилання на вигляд якоря, що має ідеальний сенс, оскільки це дає можливість виду якоря збирати сміття навіть тоді, коли екземпляр підказки живе довше, ніж його вид прив’язки.

Щасливого кодування! :)


Дозвольте поділитися одним робочим прикладом WeakReferenceкласу. Це невеликий фрагмент коду з віджетом Framework Framework, який називається 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);
}

Одне останнє. Я також хотів знати робочий приклад програми WeakReferenceAndroid, і я міг знайти деякі зразки в його офіційних прикладних програмах. Але я дійсно не міг зрозуміти використання деяких із них. Наприклад, програми ThreadSample та DisplayingBitmaps використовують WeakReferenceу своєму коді, але , виконавши кілька тестів, я виявив, що метод get () ніколи не повертається null, тому що посилається об’єкт подання переробляється в адаптерах, а не збирається сміттям.


1
Дякую за чудові приклади - я справді відчуваю, що це має бути прийнятою відповіддю на питання. Ура!
Ackshaey Singh

@AckshaeySingh Дякую! :)
1616

16

Деякі з інших відповідей здаються неповними або занадто довгими. Ось загальна відповідь.

Як використовувати WeakReference в Java та Android

Ви можете виконати наступні дії:

  1. Створіть WeakReferenceзмінну
  2. Встановіть слабку орієнтир
  3. Використовуйте слабку орієнтир

Код

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

Примітки

  • Причиною, що вам потрібна слабка довідка, є те, що збирач сміття може утилізувати предмети, коли вони більше не потрібні. Якщо два об'єкти зберігають чітку посилання на один одного, то їх не можна збирати сміттям. Це витік пам'яті.
  • Якщо два об'єкти потребують посилання один на одного, об’єкт A (як правило, короткоживучий об'єкт) повинен мати слабке посилання на об'єкт B (як правило, довше живий об'єкт), тоді як B має чітке посилання на A. У наведеному вище прикладі MyClassбуло A і AnotherClassбув Б.
  • Альтернативою для використання a WeakReferenceє те, щоб інший клас реалізував інтерфейс. Це робиться за схемою слухача / спостерігача .

Практичний приклад


заплутане пояснення. що є // allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); }??
likejudo

@likejudo, ти маєш рацію. Я вдосконалив деякі назви змінних та методів. Як зараз?
Сурагч

вам потрібно перевірити сам weakreferenceоб’єкт у doSomethingфункції, щоб не бути nullперед викликом getфункції.
Беруз.М

7

"Канонізоване" зіставлення - це те, коли ви зберігаєте один екземпляр розглянутого об'єкта в пам'яті, а всі інші шукають цей конкретний екземпляр за допомогою покажчиків або механізму дещо. Тут можуть допомогти посилання на слабкі місця. Коротка відповідь полягає в тому, що об'єкти 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.