Come usare WeakReference nello sviluppo Java e Android?


Risposte:


229

L'uso di a WeakReferencein Android non è diverso dall'uso di uno in Java semplicemente vecchio. Ecco una grande guida che fornisce una spiegazione dettagliata: Comprensione dei riferimenti deboli .

Dovresti pensare di usarne uno ogni volta che hai bisogno di un riferimento a un oggetto, ma non vuoi quel riferimento per proteggere l'oggetto dal Garbage Collector. Un classico esempio è una cache che si desidera raccogliere in modo inutile quando l'utilizzo della memoria diventa troppo elevato (spesso implementato con WeakHashMap).

Assicurati di controllare SoftReferencee PhantomReferencepure.

EDIT: Tom ha sollevato alcune preoccupazioni sull'implementazione di una cache con WeakHashMap. Ecco un articolo che illustra i problemi: WeakHashMap non è una cache!

Tom ha ragione nel dire che ci sono state lamentele per le scarse prestazioni di Netbeans dovute alla WeakHashMapmemorizzazione nella cache.

Continuo a pensare che sarebbe una buona esperienza di apprendimento implementare una cache WeakHashMape poi confrontarla con la propria cache implementata manualmente SoftReference. Nel mondo reale, probabilmente non useresti nessuna di queste soluzioni, dal momento che ha più senso usare una libreria di terze parti come Apache JCS .


15
No! No! No! WeakHashMapusato come cache è fatale. Le voci possono essere rimosse non appena vengono create. Questo probabilmente non accadrà durante il test, ma potrebbe anche essere in uso. Da notare che NetBeans può essere arrestato al 100% dalla CPU.
Tom Hawtin - tackline

3
@Tom ho aggiornato la mia risposta. Ad essere sinceri, però, sono stato tecnicamente corretto sul fatto che le cache sono spesso implementate WeakHashMapanche se hai ragione sul fatto che si tratta di una scelta sbagliata;)
dbyrne

1
Ottima risposta di dbyrne. Grazie per questo. Non vedo motivo per cui Chris non accetti questa risposta.
San

@dbyrne Sto usando oggetti nella mia attività come GridView, ImageView o BaseAdapter. Nel metodo onDestroy, quando finisco l'attività, devo fare qualcosa con questi oggetti usando Weak / SoftReferences? O il sistema pulisce automaticamente questa memoria di questo oggetto?
beni

4
collegamento interrotto a java.net
Suragch

64

[EDIT2] Ho trovato un altro buon esempio di WeakReference. Elaborazione di bitmap dalla pagina Discussione dell'interfaccia utente in Visualizzazione di bitmap Guida all'addestramento efficiente , mostra un utilizzo di WeakReferencein 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);
            }
        }
    }
}

Dice,

Il riferimento debole a ImageView assicura che AsyncTask non impedisca a ImageView e tutto ciò a cui fa riferimento di essere raccolto . Non c'è garanzia che ImageView sia ancora in giro al termine dell'attività, quindi è necessario controllare anche il riferimento in onPostExecute (). ImageView potrebbe non esistere più, ad esempio se l'utente si allontana dall'attività o se si verifica una modifica della configurazione prima del completamento dell'attività.

Buona programmazione!


[EDIT] Ho trovato un ottimo esempio di WeakReferenceda facebook-android-sdk . ToolTipPopup classe non è altro che una semplice classe di widget che mostra una descrizione comandi sopra la vista di ancoraggio. Ho catturato uno screenshot.

screenshot delizioso

La classe è davvero semplice (circa 200 righe) e degna di essere vista. In quella classe, la WeakReferenceclasse viene utilizzata per contenere il riferimento alla vista di ancoraggio, il che ha perfettamente senso, perché rende possibile che la vista di ancoraggio venga raccolta in modo errato anche quando un'istanza di tooltip vive più a lungo della sua vista di ancoraggio.

Buona programmazione! :)


Vorrei condividere un esempio funzionante di WeakReferenceclasse. È un piccolo frammento di codice dal widget del framework Android chiamato AutoCompleteTextView.

In breve, la WeakReference classe viene utilizzata per contenere l' View oggetto per impedire la perdita di memoria in questo esempio.

Copierò e incollerò semplicemente la classe PopupDataSetObserver, che è una classe nidificata di AutoCompleteTextView. È davvero semplice e i commenti spiegano bene la lezione. Buona programmazione! :)

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

E PopupDataSetObserverviene utilizzato nell'adattatore di impostazione.

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

Un'ultima cosa. Volevo anche sapere un esempio funzionante WeakReferencedell'applicazione Android, e ho potuto trovare alcuni esempi nelle sue applicazioni campione ufficiali. Ma non riuscivo davvero a capire alcuni dei loro usi. Ad esempio, ThreadSample e DisplayingBitmaps applicazioni utilizzano WeakReferencenel codice, ma dopo aver eseguito vari test, ho trovato che il metodo get () non ritorna null, perché fa riferimento sottofinestra viene riciclato in adattatori, piuttosto che garbage collection.


1
Grazie per i grandi esempi - Sento davvero che questa dovrebbe essere la risposta accettata alla domanda. Saluti!
Ackshaey Singh,

@AckshaeySingh Grazie! :)
김준호

16

Alcune delle altre risposte sembrano incomplete o eccessivamente lunghe. Ecco una risposta generale.

Come usare WeakReference in Java e Android

Puoi fare i seguenti passi:

  1. Crea una WeakReferencevariabile
  2. Imposta il riferimento debole
  3. Usa il riferimento debole

Codice

MyClassha un riferimento debole a 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
    }

}

AnotherClassha un forte riferimento a 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);
    }
}

Appunti

  • Il motivo per cui è necessario un riferimento debole è che Garbage Collector possa disporre degli oggetti quando non sono più necessari. Se due oggetti mantengono un forte riferimento reciproco, non possono essere raccolti in modo inutile. Questa è una perdita di memoria.
  • Se due oggetti devono fare riferimento l'un l'altro, l'oggetto A (generalmente l'oggetto vissuto più breve) dovrebbe avere un riferimento debole all'oggetto B (generalmente l'oggetto vissuto più a lungo), mentre B ha un forte riferimento ad A. Nell'esempio sopra, MyClassera A eAnotherClass era B.
  • Un'alternativa all'utilizzo di a WeakReferenceè avere un'altra classe implementare un'interfaccia. Questo viene fatto nel modello di ascolto / osservatore .

Esempio pratico


spiegazione confusa. cos'è // allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); }??
likejudo,

@likejudo, hai ragione. Ho migliorato alcune variabili e la denominazione del metodo. Com'è adesso?
Suragch

è necessario controllare l' weakreferenceoggetto stesso nella doSomethingfunzione per non essere nullprima di chiamare la getfunzione.
Behrouz.M

7

Una mappatura "canonicalizzata" è quella in cui si tiene in memoria un'istanza dell'oggetto in questione e tutte le altre cercano quella particolare istanza tramite puntatori o meccanismo simile. Questo è dove i riferimenti deboli possono aiutare. La risposta breve è che gli oggetti WeakReference possono essere utilizzati per creare puntatori agli oggetti nel sistema pur consentendo a tali oggetti di essere recuperati dal Garbage Collector dopo che sono usciti dall'ambito. Ad esempio se avessi un codice come questo:

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

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

Qualsiasi oggetto che registro non verrà mai reclamato dal GC perché è presente un riferimento memorizzato nel set di registeredObjects. D'altra parte se lo faccio:

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

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

Quindi, quando il GC vuole recuperare gli oggetti nel Set, sarà in grado di farlo. È possibile utilizzare questa tecnica per la memorizzazione nella cache, la catalogazione, ecc. Vedere di seguito i riferimenti a discussioni molto più approfondite su GC e cache.

Rif: Garbage Collector e WeakReference

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.