Jak używać WeakReference w programowaniu w Javie i Androidzie?

166

Jestem programistą Java od 2 lat.

Ale nigdy nie napisałem WeakReference w moim kodzie. Jak używać WeakReference, aby zwiększyć wydajność mojej aplikacji, zwłaszcza aplikacji na Androida?

Chris
źródło
Może istnieć różnica dla Androida: stackoverflow.com/questions/299659/ ...
Pacerier

Odpowiedzi:

229

Używanie WeakReferencew Androidzie nie różni się niczym od używania w zwykłej starej Javie. Oto świetny przewodnik, który zawiera szczegółowe wyjaśnienie: Zrozumienie słabych odniesień .

Powinieneś pomyśleć o użyciu takiego, gdy potrzebujesz odwołania do obiektu, ale nie chcesz, aby to odwołanie chroniło obiekt przed odśmiecaniem pamięci. Klasycznym przykładem jest pamięć podręczna, która ma zostać usunięta, gdy użycie pamięci stanie się zbyt wysokie (często implementowane z WeakHashMap).

Koniecznie sprawdź SoftReferencei PhantomReferenceteż.

EDYCJA: Tom zgłosił pewne obawy dotyczące implementacji pamięci podręcznej z WeakHashMap. Oto artykuł opisujący problemy: WeakHashMap nie jest pamięcią podręczną!

Tom ma rację, że były skargi na słabą wydajność Netbeans z powodu WeakHashMapbuforowania.

Nadal uważam, że warto byłoby zaimplementować pamięć podręczną z, WeakHashMapa następnie porównać ją z własną ręcznie zwijaną pamięcią podręczną zaimplementowaną w programie SoftReference. W prawdziwym świecie prawdopodobnie nie użyłbyś żadnego z tych rozwiązań, ponieważ bardziej sensowne jest użycie biblioteki innej firmy, takiej jak Apache JCS .

dbyrne
źródło
15
Nie! Nie! Nie! WeakHashMapużywany jako pamięć podręczna jest śmiertelny. Wpisy można usunąć zaraz po ich utworzeniu. Prawdopodobnie nie wydarzy się to podczas testowania, ale może się zdarzyć podczas użytkowania. Warto zauważyć, że NetBeans może zostać przez to doprowadzony do efektywnego zatrzymania procesora w 100%.
Tom Hawtin - tackline
3
@Tom Zaktualizowałem moją odpowiedź. Aby być uczciwym, technicznie miałem rację, że pamięci podręczne SĄ często implementowane z, WeakHashMapnawet jeśli masz rację, że jest to zły wybór;)
dbyrne
1
Doskonała odpowiedź dbyrne'a. Dzięki za to. Nie widzę powodu, dla którego chris nie miałby akceptować tej odpowiedzi.
san
@dbyrne Używam obiektów w moim działaniu, takich jak GridView, ImageView lub BaseAdapter. W metodzie onDestroy, kiedy kończę działanie, czy muszę coś zrobić z tymi obiektami przy użyciu Weak / SoftReferences? Czy system automatycznie wyczyści tę pamięć tego obiektu?
beni
4
uszkodzony link do java.net
Suragch
64

[EDIT2] Znalazłem inny dobry przykład WeakReference. Przetwarzania bitmapy Off wątku UI stronie w Wyświetlanie bitmapy Skutecznie Training Guide, pokazuje jedno użycie WeakReferencew 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);
            }
        }
    }
}

To mówi,

WeakReference to the ImageView zapewnia, że ​​AsyncTask nie zapobiega wyrzucaniu elementów bezużytecznych ImageView i wszystkiego, do czego się odwołuje . Nie ma gwarancji, że po zakończeniu zadania ImageView nadal jest w pobliżu, dlatego należy również sprawdzić odwołanie w onPostExecute (). ImageView może już nie istnieć, jeśli na przykład użytkownik opuści działanie lub jeśli zmiana konfiguracji nastąpi przed zakończeniem zadania.

Miłego kodowania!


[EDYCJA] Znalazłem naprawdę dobry przykład WeakReferencez facebook-android-sdk . Klasa ToolTipPopup to nic innego jak prosta klasa widżetu, która wyświetla podpowiedź nad widokiem zakotwiczenia. Zrobiłem zrzut ekranu.

przepyszny zrzut ekranu

Klasa jest naprawdę prosta (około 200 linii) i godna obejrzenia. W tej klasie WeakReferenceklasa jest używana do przechowywania odwołania do widoku zakotwiczenia, co ma sens, ponieważ umożliwia gromadzenie widoku zakotwiczenia jako śmieci nawet wtedy, gdy instancja podpowiedzi trwa dłużej niż jej widok zakotwiczenia.

Miłego kodowania! :)


Pozwólcie, że podzielę się jednym działającym przykładem WeakReferenceklasy. To mały fragment kodu z widgetu platformy Android o nazwie AutoCompleteTextView.

Krótko mówiąc, WeakReference klasa jest używana do przechowywania View obiektu, aby zapobiec wyciekowi pamięci w tym przykładzie.

Po prostu skopiuję i wkleję klasę PopupDataSetObserver, która jest zagnieżdżoną klasą AutoCompleteTextView. To naprawdę proste, a komentarze dobrze wyjaśniają zajęcia. Miłego kodowania! :)

    /**
     * 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());
        }
    };
}

I PopupDataSetObserverjest używany do ustawiania adaptera.

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

Ostatnia rzecz. Chciałem również poznać działający przykład WeakReferenceaplikacji na Androida i mogłem znaleźć kilka próbek w jej oficjalnych przykładowych aplikacjach. Ale naprawdę nie mogłem zrozumieć zastosowania niektórych z nich. Na przykład aplikacje ThreadSample i DisplayingBitmaps używają WeakReferencew swoim kodzie, ale po wykonaniu kilku testów okazało się, że metoda get () nigdy nie zwraca null, ponieważ obiekt widoku, do którego istnieje odwołanie, jest przetwarzany w adapterach, a nie zbierany jako śmieci.

김준호
źródło
1
Dziękuję za świetne przykłady - naprawdę uważam, że powinna to być akceptowana odpowiedź na to pytanie. Twoje zdrowie!
Ackshaey Singh
@AckshaeySingh Thanks! :)
김준호
16

Niektóre z pozostałych odpowiedzi wydają się niepełne lub zbyt długie. Oto ogólna odpowiedź.

Jak używać WeakReference w Javie i Androidzie

Możesz wykonać następujące kroki:

  1. Utwórz WeakReferencezmienną
  2. Ustaw słabe odniesienie
  3. Użyj słabego odniesienia

Kod

MyClassma słabe odniesienie do 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
    }

}

AnotherClassma silne odniesienie do 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);
    }
}

Uwagi

  • Powodem, dla którego potrzebujesz słabego odniesienia jest to, że Garbage Collector może pozbyć się obiektów, gdy nie są już potrzebne. Jeśli dwa obiekty zachowują do siebie silne odniesienia, nie można ich wyrzucać do pamięci. To wyciek pamięci.
  • Jeśli dwa obiekty wymagają odniesienia do siebie nawzajem, obiekt A (generalnie krótszy żyjący obiekt) powinien mieć słabe odniesienie do obiektu B (ogólnie obiekt o dłuższej żywotności), podczas gdy B ma silne odniesienie do A. W powyższym przykładzie MyClassA i AnotherClassbył B.
  • Alternatywą dla używania a WeakReferencejest implementacja interfejsu przez inną klasę. Odbywa się to we wzorcu Listener / Observer .

Praktyczny przykład

Suragch
źródło
mylące wyjaśnienie. co to jest // allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); }??
likejudo
@likejudo, masz rację. Poprawiłem niektóre nazewnictwo zmiennych i metod. Jak jest teraz?
Suragch
musisz sprawdzić, czy weakreferencesam obiekt w doSomethingfunkcji nie występuje nullprzed wywołaniem getfunkcji.
Behrouz.M
7

Mapowanie „kanoniczne” polega na tym, że zachowuje się w pamięci jedną instancję danego obiektu, a wszystkie inne wyszukują tę konkretną instancję za pomocą wskaźników lub innego mechanizmu. Tutaj mogą pomóc słabe referencje. Krótka odpowiedź jest taka, że obiektów WeakReference można używać do tworzenia wskaźników do obiektów w systemie, jednocześnie umożliwiając odzyskiwanie tych obiektów przez moduł odśmiecania pamięci, gdy wyjdą poza zakres. Na przykład gdybym miał taki kod:

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

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

Żaden obiekt, który zarejestruję, nigdy nie zostanie odzyskany przez GC, ponieważ istnieje do niego odniesienie przechowywane w zbiorze registeredObjects. Z drugiej strony, jeśli to zrobię:

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

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

Wtedy, gdy GC zechce odzyskać obiekty ze zbioru, będzie w stanie to zrobić. Możesz użyć tej techniki do buforowania, katalogowania itp. Poniżej znajdziesz odniesienia do znacznie bardziej szczegółowych dyskusji na temat GC i buforowania.

Ref: Garbage collector i WeakReference

Akshay
źródło