Odpowiedzi:
Używanie WeakReference
w 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ź SoftReference
i PhantomReference
też.
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 WeakHashMap
buforowania.
Nadal uważam, że warto byłoby zaimplementować pamięć podręczną z, WeakHashMap
a 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 .
WeakHashMap
uż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%.
WeakHashMap
nawet jeśli masz rację, że jest to zły wybór;)
[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 WeakReference
w 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 WeakReference
z 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.
Klasa jest naprawdę prosta (około 200 linii) i godna obejrzenia. W tej klasie WeakReference
klasa 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 WeakReference
klasy. 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 PopupDataSetObserver
jest 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 WeakReference
aplikacji 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ą WeakReference
w 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.
Niektóre z pozostałych odpowiedzi wydają się niepełne lub zbyt długie. Oto ogólna odpowiedź.
Możesz wykonać następujące kroki:
WeakReference
zmiennąMyClass
ma 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
}
}
AnotherClass
ma 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);
}
}
MyClass
A i AnotherClass
był B.WeakReference
jest implementacja interfejsu przez inną klasę. Odbywa się to we wzorcu Listener / Observer .// allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); }
??
weakreference
sam obiekt w doSomething
funkcji nie występuje null
przed wywołaniem get
funkcji.
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.