¿Cómo usar WeakReference en el desarrollo de Java y Android?

166

He sido desarrollador de Java durante 2 años.

Pero nunca he escrito una WeakReference en mi código. ¿Cómo usar WeakReference para hacer que mi aplicación sea más eficiente, especialmente la aplicación de Android?

Chris
fuente
Puede haber una diferencia para Android: stackoverflow.com/questions/299659/…
Pacerier

Respuestas:

229

Usar un WeakReferenceen Android no es diferente de usar uno en Java antiguo. Aquí hay una gran guía que brinda una explicación detallada: Comprender las referencias débiles .

Debería pensar en usar uno cada vez que necesite una referencia a un objeto, pero no desea que esa referencia proteja el objeto del recolector de basura. Un ejemplo clásico es un caché en el que desea recolectar basura cuando el uso de memoria es demasiado alto (a menudo implementado conWeakHashMap ).

Asegúrese de revisar SoftReferenceyPhantomReference también.

EDITAR: Tom ha planteado algunas preocupaciones sobre la implementación de un caché con WeakHashMap. Aquí hay un artículo que presenta los problemas: WeakHashMap no es un caché!

Tom tiene razón en que ha habido quejas sobre el bajo rendimiento de Netbeans debido aWeakHashMap almacenamiento caché.

Todavía creo que sería una buena experiencia de aprendizaje implementar un caché WeakHashMapy luego compararlo con su propio caché enrollado a mano implementado SoftReference. En el mundo real, probablemente no usaría ninguna de estas soluciones, ya que tiene más sentido usar una biblioteca de terceros como Apache JCS .

dbyrne
fuente
15
¡No! ¡No! ¡No! WeakHashMapusado como caché es fatal. Las entradas se pueden eliminar tan pronto como se crean. Esto probablemente no sucederá cuando esté realizando la prueba, pero puede ocurrir cuando esté en uso. Es de destacar que NetBeans puede llegar a una parada efectiva del 100% de la CPU con esto.
Tom Hawtin - tackline
3
@ Tom He actualizado mi respuesta. Sin embargo, para ser justos, era técnicamente correcto que las memorias caché a menudo se implementan, WeakHashMapincluso si usted es correcto, es una mala elección;)
dbyrne
1
Excelente respuesta de dbyrne. Gracias por esto. No veo ninguna razón para que Chris no acepte esta respuesta.
san
@dbyrne Estoy usando objetos en mi actividad como GridView, ImageView o BaseAdapter. En el método onDestroy, cuando termino la actividad, ¿necesito hacer algo con estos objetos usando Weak / SoftReferences? ¿O el sistema limpia automáticamente esta memoria de este objeto?
beni
44
enlace roto a java.net
Suragch
64

[EDIT2] Encontré otro buen ejemplo de WeakReference. Procesar mapas de bits fuera de la página de subprocesos de la interfaz de usuario en la guía de capacitación Visualización eficiente de WeakReferencemapas de bits , muestra un uso de 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,

La WeakReference a ImageView garantiza que AsyncTask no impida que se recoja basura ImageView y cualquier cosa a la que haga referencia . No hay garantía de que ImageView siga existiendo cuando finalice la tarea, por lo que también debe verificar la referencia en onPostExecute (). Es posible que ImageView ya no exista, si, por ejemplo, el usuario se aleja de la actividad o si se produce un cambio de configuración antes de que finalice la tarea.

¡Feliz codificación!


[EDIT] me encontré con un muy buen ejemplo de WeakReferencedesde facebook-android-sdk . La clase ToolTipPopup no es más que una clase de widget simple que muestra información sobre herramientas sobre la vista de anclaje. Capturé una captura de pantalla.

captura de pantalla deliciosa

La clase es realmente simple (alrededor de 200 líneas) y digna de ver. En esa clase,WeakReference clase se usa para mantener referencia a la vista de anclaje, lo que tiene mucho sentido, ya que hace posible que la vista de anclaje se recolecte basura incluso cuando una instancia de información sobre herramientas dura más que su vista de anclaje.

¡Feliz codificación! :)


Permítanme compartir un ejemplo WeakReferencepráctico de clase. Es un pequeño fragmento de código del widget de marco de Android llamado AutoCompleteTextView.

En resumen, la WeakReference clase se usa para contener View objetos para evitar pérdidas de memoria en este ejemplo.

Copiaré y pegaré la clase PopupDataSetObserver, que es una clase anidada de AutoCompleteTextView. Es realmente simple y los comentarios explican bien la clase. ¡Feliz codificación! :)

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

Y PopupDataSetObserverse usa para configurar el adaptador.

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

Una última cosa. También quería saber ejemplos de trabajo WeakReferenceen aplicaciones de Android, y pude encontrar algunas muestras en sus aplicaciones de muestra oficiales. Pero realmente no podía entender el uso de algunos de ellos. Por ejemplo, ThreadSample y DisplayingBitmaps aplicaciones utilizan WeakReferenceen su código, pero después de correr varias pruebas, he encontrado que el método get () no vuelve nunca null, porque la vista referenciada objeto se recicla en los adaptadores, en lugar de basura recogida.

김준호
fuente
1
Gracias por los excelentes ejemplos. Realmente siento que esta debería ser la respuesta aceptada para la pregunta. ¡Salud!
Ackshaey Singh
@AckshaeySingh ¡Gracias! :)
김준호
16

Algunas de las otras respuestas parecen incompletas o demasiado largas. Aquí hay una respuesta general.

Cómo usar WeakReference en Java y Android

Puedes hacer los siguientes pasos:

  1. Crear un WeakReference variable
  2. Establecer la referencia débil
  3. Usa la referencia débil

Código

MyClasstiene una referencia débil 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
    }

}

AnotherClasstiene una fuerte referencia 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);
    }
}

Notas

  • La razón por la que necesita una referencia débil es para que el recolector de basura pueda deshacerse de los objetos cuando ya no los necesite. Si dos objetos retienen una fuerte referencia entre sí, entonces no se pueden recolectar basura. Esta es una pérdida de memoria.
  • Si dos objetos necesitan referenciarse entre sí, el objeto A (generalmente el objeto de vida más corta) debe tener una referencia débil al objeto B (generalmente el objeto de vida más larga), mientras que B tiene una fuerte referencia a A. En el ejemplo anterior, MyClassera A y AnotherClassfue B.
  • Una alternativa al uso de a WeakReferencees hacer que otra clase implemente una interfaz. Esto se hace en el patrón de escucha / observador .

Ejemplo práctico

Suragch
fuente
explicación confusa que es // allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); }??
likejudo
@likejudo, tienes razón. Mejoré algunos de los nombres de variables y métodos. ¿Cómo está ahora?
Suragch
debe verificar el weakreferenceobjeto en sí mismo en la doSomethingfunción para que no esté nullantes de llamar a la getfunción.
Behrouz.M
7

Un mapeo "canonicalizado" es donde mantiene una instancia del objeto en cuestión en la memoria y todas las demás buscan esa instancia en particular a través de punteros o algún mecanismo. Aquí es donde las referencias de los débiles pueden ayudar. La respuesta corta es que los objetos WeakReference se pueden usar para crear punteros a objetos en su sistema y al mismo tiempo permitir que esos objetos sean reclamados por el recolector de basura una vez que pasan del alcance. Por ejemplo, si tuviera un código como este:

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

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

Cualquier objeto que registre nunca será reclamado por el GC porque hay una referencia a él almacenado en el conjunto de registeredObjects. Por otro lado si hago esto:

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

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

Luego, cuando el GC quiera reclamar los objetos en el Conjunto, podrá hacerlo. Puede utilizar esta técnica para el almacenamiento en caché, la catalogación, etc. Consulte a continuación las referencias a debates mucho más detallados sobre GC y almacenamiento en caché.

Ref: recolector de basura y referencia débil

Akshay
fuente