Как использовать WeakReference в разработке Java и Android?

166

Я был разработчиком Java в течение 2 лет.

Но я никогда не писал WeakReference в своем коде. Как использовать WeakReference, чтобы сделать мое приложение более эффективным, особенно приложение Android?

Крис
источник
Там может быть разница для Android: stackoverflow.com/questions/299659/…
Pacerier

Ответы:

229

Использование WeakReferenceв Android ничем не отличается от использования в простой старой Java. Вот отличное руководство, которое дает подробное объяснение: Понимание слабых ссылок .

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

Обязательно проверьте SoftReferenceи PhantomReferenceтак же.

РЕДАКТИРОВАТЬ: Том поднял некоторые проблемы по поводу реализации кеш с WeakHashMap. Вот статья с изложением проблем: WeakHashMap - это не кеш!

Том прав, что были жалобы на плохую производительность NetBeans из-за WeakHashMapкэширования.

Я до сих пор думаю, что было бы неплохо изучить кэш с помощью кеша, WeakHashMapа затем сравнить его с вашим собственным кешем, реализованным вручную SoftReference. В реальном мире вы, вероятно, не использовали бы ни одно из этих решений, поскольку более разумно использовать стороннюю библиотеку, такую ​​как Apache JCS .

dbyrne
источник
15
Нет! Нет! Нет! WeakHashMapиспользуется как кеш фатально. Записи могут быть удалены, как только они созданы. Это, вероятно, не произойдет, когда вы тестируете, но вполне может быть при использовании. Следует отметить, что NetBeans может привести к 100% эффективной остановке ЦП.
Том Хотин - снасть
3
@ Том я обновил свой ответ. Честно говоря, я был технически прав, что кеши часто используются, WeakHashMapдаже если вы правы, что это плохой выбор;)
dbyrne
1
Отличный ответ от dbyrne. Спасибо за это. Я не вижу причин для Крис, чтобы не принять этот ответ.
Сан
@dbyrne Я использую объекты в своей деятельности, такие как GridView, ImageView или BaseAdapter. В методе onDestroy, когда я заканчиваю действие, нужно ли что-то делать с этими объектами, используя Weak / SoftReferences? Или система автоматически очистит эту память от этого объекта?
Бени
4
неработающая ссылка на java.net
Сурагч
64

[EDIT2] Я нашел еще один хороший пример WeakReference. Обработка растровых изображений На странице « Поток пользовательского интерфейса» в разделе « Отображение растровых изображений» в учебном руководстве « Эффективно» показано одно использование WeakReferenceAsyncTask.

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

Удачного кодирования!


[РЕДАКТИРОВАТЬ] Я нашел действительно хороший пример WeakReferenceиз Facebook-Android-SDK . Класс 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);
}

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

김준호
источник
1
Спасибо за отличные примеры - я действительно чувствую, что это должен быть принятый ответ на вопрос. Ура!
Акшаи Сингх
@AckshaeySingh Спасибо! :)
김준호
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был Б.
  • Альтернативой использованию WeakReferenceявляется использование другого класса для реализации интерфейса. Это делается в шаблоне слушателя / наблюдателя .

Практический пример

Suragch
источник
смутное объяснение. что это // allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); }??
Likejudo
@likejudo, ты прав. Я улучшил некоторые имена переменных и методов. Как оно сейчас?
Сурагч
вам нужно проверить сам weakreferenceобъект в doSomethingфункции, чтобы не быть nullперед вызовом getфункции.
Behrouz.M
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 захочет восстановить объекты в наборе, он сможет это сделать. Вы можете использовать эту технику для кэширования, каталогизации и т. Д. См. Ниже ссылки на более подробные обсуждения GC и кэширования.

Ссылка: сборщик мусора и WeakReference

Акшай
источник