Comment utiliser WeakReference dans le développement Java et Android?

166

Je suis développeur java depuis 2 ans.

Mais je n'ai jamais écrit de WeakReference dans mon code. Comment utiliser WeakReference pour rendre mon application plus efficace en particulier l'application Android?

Chris
la source
Il pourrait y avoir une différence pour Android: stackoverflow.com/questions/299659/…
Pacerier

Réponses:

229

Utiliser un WeakReferencedans Android n'est pas différent de celui en utilisant un dans l'ancien Java. Voici un excellent guide qui donne une explication détaillée: Comprendre les références faibles .

Vous devriez penser à en utiliser un chaque fois que vous avez besoin d'une référence à un objet, mais vous ne voulez pas que cette référence protège l'objet du garbage collector. Un exemple classique est un cache que vous souhaitez récupérer lorsque l'utilisation de la mémoire devient trop élevée (souvent implémenté avec WeakHashMap).

Assurez-vous de vérifier SoftReferenceet PhantomReferenceaussi.

EDIT: Tom a soulevé quelques inquiétudes concernant l'implémentation d'un cache avec WeakHashMap. Voici un article exposant les problèmes: WeakHashMap n'est pas un cache!

Tom a raison de dire qu'il y a eu des plaintes concernant les mauvaises performances de Netbeans en raison deWeakHashMap mise cache.

Je pense toujours que ce serait une bonne expérience d'apprentissage d'implémenter un cache avec WeakHashMap, puis de le comparer à votre propre cache mis en œuvre à la main avec SoftReference. Dans le monde réel, vous n'utiliseriez probablement aucune de ces solutions, car il est plus logique d'utiliser une bibliothèque tierce comme Apache JCS .

dbyrne
la source
15
Non! Non! Non! WeakHashMaputilisé comme cache est fatal. Les entrées peuvent être supprimées dès leur création. Cela ne se produira probablement pas lorsque vous testez, mais peut très bien être utilisé. Il est à noter que NetBeans peut être amené à un arrêt effectif à 100% du processeur par cela.
Tom Hawtin - tackline
3
@Tom J'ai mis à jour ma réponse. Pour être juste cependant, j'avais techniquement raison de dire que les caches SONT souvent implémentés avec WeakHashMapmême si vous avez raison de dire que c'est un mauvais choix;)
dbyrne
1
Excellente réponse de dbyrne. Merci pour cela. Je ne vois aucune raison pour que le chris n'accepte pas cette réponse.
san
@dbyrne J'utilise des objets dans mon activité comme GridView, ImageView ou BaseAdapter. Dans la méthode onDestroy, lorsque je termine l'activité, dois-je faire quelque chose avec ces objets en utilisant Weak / SoftReferences? Ou le système nettoie automatiquement cette mémoire de cet objet?
beni
4
lien cassé vers java.net
Suragch
64

[EDIT2] J'ai trouvé un autre bon exemple de WeakReference. Traitement des bitmaps en dehors de la page de thread de l'interface utilisateur dans le guide de formation Afficher efficacement les bitmaps , montre une utilisation de WeakReferencedans 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);
            }
        }
    }
}

Ça dit,

Le WeakReference à ImageView garantit que l'AsyncTask n'empêche pas ImageView et tout ce qu'il référence d'être récupéré . Il n'y a aucune garantie que ImageView est toujours là lorsque la tâche se termine, vous devez donc également vérifier la référence dans onPostExecute (). ImageView peut ne plus exister si, par exemple, l'utilisateur quitte l'activité ou si une modification de configuration se produit avant la fin de la tâche.

Bon codage!


[EDIT] J'ai trouvé un très bon exemple de WeakReferencede facebook-android-sdk . La classe ToolTipPopup n'est rien d'autre qu'une simple classe de widget qui affiche une info-bulle au-dessus de la vue d'ancrage. J'ai pris une capture d'écran.

capture d'écran succulente

La classe est vraiment simple (environ 200 lignes) et mérite d'être regardée. Dans cette classe,WeakReference classe est utilisée pour contenir une référence à la vue d'ancrage, ce qui est parfaitement logique, car elle permet à la vue d'ancrage d'être récupérée même lorsqu'une instance d'info-bulle vit plus longtemps que sa vue d'ancrage.

Bon codage! :)


Permettez-moi de partager un exemple pratique de WeakReferenceclasse. C'est un petit extrait de code du widget du framework Android appelé AutoCompleteTextView.

En bref, la WeakReference classe est utilisée pour contenir un View objet afin d'éviter une fuite de mémoire dans cet exemple.

Je vais simplement copier-coller la classe PopupDataSetObserver, qui est une classe imbriquée de AutoCompleteTextView. C'est vraiment simple et les commentaires expliquent bien la classe. Bon codage! :)

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

Et le PopupDataSetObserverest utilisé dans l'adaptateur de réglage.

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

Une dernière chose. Je voulais également connaître des exemples de travail WeakReferencedans une application Android, et j'ai pu trouver des exemples dans ses exemples d'applications officiels. Mais je ne pouvais vraiment pas comprendre l'utilisation de certains d'entre eux. Par exemple, les applications ThreadSample et DisplayingBitmaps utilisent WeakReferencedans son code, mais après avoir exécuté plusieurs tests, j'ai découvert que la méthode get () ne retourne jamais null, car l'objet de vue référencé est recyclé dans les adaptateurs, plutôt que collecté.

김준호
la source
1
Merci pour les excellents exemples - je pense vraiment que cela devrait être la réponse acceptée à la question. À votre santé!
Ackshaey Singh
@AckshaeySingh Merci! :)
김준호
16

Certaines des autres réponses semblent incomplètes ou trop longues. Voici une réponse générale.

Comment utiliser WeakReference en Java et Android

Vous pouvez suivre les étapes suivantes:

  1. Créer une WeakReferencevariable
  2. Définir la référence faible
  3. Utilisez la référence faible

Code

MyClassa une faible référence à 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
    }

}

AnotherClassa une forte référence à 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);
    }
}

Remarques

  • La raison pour laquelle vous avez besoin d'une référence faible est que le garbage collector puisse supprimer les objets lorsqu'ils ne sont plus nécessaires. Si deux objets conservent une forte référence l'un à l'autre, ils ne peuvent pas être récupérés. Il s'agit d'une fuite de mémoire.
  • Si deux objets doivent se référencer l'un à l'autre, l'objet A (généralement l'objet à vie plus courte) doit avoir une référence faible à l'objet B (généralement l'objet à vie plus longue), tandis que B a une référence forte à A. Dans l'exemple ci-dessus, MyClassétait A etAnotherClass était B.
  • Une alternative à l'utilisation de a WeakReferenceconsiste à demander à une autre classe d'implémenter une interface. Ceci est fait dans le modèle Listener / Observer .

Exemple pratique

Suragch
la source
explication déroutante. qu'est ce que c'est // allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); }??
likejudo
@likejudo, vous avez raison. J'ai amélioré certains des noms de variables et de méthodes. Comment est-ce maintenant?
Suragch
vous devez vérifier l' weakreferenceobjet lui-même en doSomethingfonction pour ne pas être nullavant d'appeler getfunction.
Behrouz.M
7

Un mappage «canonisé» est l'endroit où vous gardez une instance de l'objet en question en mémoire et que toutes les autres recherchent cette instance particulière via des pointeurs ou un tel mécanisme. C'est là que les références faibles peuvent aider. La réponse courte est que les objets WeakReference peuvent être utilisés pour créer des pointeurs vers des objets dans votre système tout en permettant à ces objets d'être récupérés par le ramasse-miettes une fois qu'ils sont hors de portée. Par exemple, si j'avais un code comme celui-ci:

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

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

Tout objet que j'enregistre ne sera jamais récupéré par le GC car il y a une référence à celui-ci stockée dans l'ensemble de registeredObjects. D'un autre côté si je fais ceci:

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

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

Ensuite, lorsque le GC voudra récupérer les objets de l'ensemble, il pourra le faire. Vous pouvez utiliser cette technique pour la mise en cache, le catalogage, etc. Voir ci-dessous pour des références à des discussions beaucoup plus approfondies sur GC et la mise en cache.

Réf: Garbage collector et WeakReference

Akshay
la source