Como usar o WeakReference no desenvolvimento Java e Android?

166

Sou desenvolvedor java há 2 anos.

Mas eu nunca escrevi uma WeakReference no meu código. Como usar o WeakReference para tornar meu aplicativo mais eficiente, especialmente o aplicativo Android?

Chris
fonte
Pode haver uma diferença para o Android: stackoverflow.com/questions/299659/…
Pacerier 19/09/17

Respostas:

229

Usar um WeakReferenceno Android não é diferente de usar um no Java antigo comum. Aqui está um ótimo guia que fornece uma explicação detalhada: Noções básicas sobre referências fracas .

Você deve pensar em usar um sempre que precisar de uma referência a um objeto, mas não deseja que essa referência proteja o objeto do coletor de lixo. Um exemplo clássico é um cache no qual você deseja coletar lixo quando o uso da memória fica muito alto (geralmente implementado com WeakHashMap).

Certifique-se de verificar SoftReferencee PhantomReferencetambém.

EDIT: Tom levantou algumas preocupações sobre a implementação de um cache com WeakHashMap. Aqui está um artigo descrevendo os problemas: WeakHashMap não é um cache!

Tom está certo de que houve reclamações sobre o fraco desempenho do Netbeans devido ao WeakHashMapcache.

Ainda acho que seria uma boa experiência de aprendizado implementar um cache WeakHashMape compará-lo com seu próprio cache implementado manualmente SoftReference. No mundo real, você provavelmente não usaria nenhuma dessas soluções, pois faz mais sentido usar uma biblioteca de terceiros como o Apache JCS .

dbyrne
fonte
15
Não! Não! Não! WeakHashMapusado como cache é fatal. As entradas podem ser removidas assim que são criadas. Provavelmente isso não acontecerá quando você estiver testando, mas pode muito bem quando estiver em uso. É importante notar que o NetBeans pode ser levado a uma parada efetiva de 100% da CPU.
Tom Hawtin - tackline
3
@ Tom Atualizei minha resposta. Para ser justo, porém, eu estava tecnicamente correto que caches são muitas vezes implementados com WeakHashMapmesmo se você está correto que é uma má escolha;)
dbyrne
1
Excelente resposta por dbyrne. Obrigado por isso. Não vejo razão para o chris não aceitar esta resposta.
san
@dbyrne Estou usando objetos na minha Atividade como GridView, ImageView ou BaseAdapter. No método onDestroy, quando termino a atividade, preciso fazer algo com esses objetos usando Weak / SoftReferences? Ou o sistema limpa automaticamente essa memória desse objeto?
beni
4
link quebrado para java.net
Suragch 7/17/17
64

[EDIT2] Encontrei outro bom exemplo de WeakReference. Processando bitmaps Fora da página Thread da interface do usuário no guia de treinamento Exibindo bitmaps com eficiência , mostra um uso WeakReferenceno 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);
            }
        }
    }
}

Diz,

O WeakReference para o ImageView garante que o AsyncTask não impeça que o ImageView e tudo o que ele referencia seja coletado como lixo . Não há garantia de que o ImageView ainda esteja disponível quando a tarefa terminar, portanto, você também deve verificar a referência em onPostExecute (). O ImageView pode não existir mais, se, por exemplo, o usuário sair da atividade ou se ocorrer uma alteração na configuração antes que a tarefa seja concluída.

Feliz codificação!


[EDIT] Encontrei um bom exemplo WeakReferencedo facebook-android-sdk . A classe ToolTipPopup nada mais é do que uma simples classe de widget que mostra a dica de ferramenta acima da visualização âncora. Eu capturei uma captura de tela.

captura de tela deliciosa

A aula é realmente simples (cerca de 200 linhas) e digna de ser vista. Nessa classe, a WeakReferenceclasse é usada para manter referência à exibição de âncora, o que faz todo sentido, porque possibilita que a exibição de âncora seja coletada como lixo, mesmo quando uma instância de dica de ferramenta vive mais que sua exibição de âncora.

Feliz codificação! :)


Deixe-me compartilhar um exemplo WeakReferenceprático de classe. É um pequeno trecho de código do widget de estrutura do Android chamado AutoCompleteTextView.

Em resumo, a WeakReference classe é usada para armazenar View objetos para evitar vazamento de memória neste exemplo.

Vou copiar e colar a classe PopupDataSetObserver, que é uma classe aninhada AutoCompleteTextView. É realmente simples e os comentários explicam bem a classe. Feliz codificação! :)

    /**
     * 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 o PopupDataSetObserveré usado na configuração do 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);
}

Uma última coisa. Eu também queria saber o exemplo de trabalho WeakReferenceno aplicativo Android e poderia encontrar alguns exemplos em seus aplicativos oficiais. Mas eu realmente não conseguia entender o uso de alguns deles. Por exemplo, os aplicativos ThreadSample e ExibindoBitmaps usam WeakReferenceem seu código, mas depois de executar vários testes, descobri que o método get () nunca retorna null, porque o objeto de exibição referenciado é reciclado em adaptadores, e não em lixo coletado.

김준호
fonte
1
Obrigado pelos ótimos exemplos - realmente sinto que essa deve ser a resposta aceita para a pergunta. Felicidades!
Ackshaey Singh
@AckshaeySingh Thanks! :)
김준호
16

Algumas das outras respostas parecem incompletas ou excessivamente longas. Aqui está uma resposta geral.

Como usar o WeakReference em Java e Android

Você pode executar as seguintes etapas:

  1. Crie uma WeakReferencevariável
  2. Defina a referência fraca
  3. Use a referência fraca

Código

MyClasstem uma referência fraca 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
    }

}

AnotherClasstem uma forte referência 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

  • O motivo pelo qual você precisa de uma referência fraca é para que o Garbage Collector possa descartar os objetos quando eles não forem mais necessários. Se dois objetos mantiverem uma forte referência um ao outro, eles não poderão ser coletados como lixo. Este é um vazamento de memória.
  • Se dois objetos precisarem fazer referência um ao outro, o objeto A (geralmente o objeto de menor duração) deve ter uma referência fraca ao objeto B (geralmente o objeto de maior duração), enquanto B tem uma forte referência a A. No exemplo acima, MyClassestava A e AnotherClassfoi B.
  • Uma alternativa ao uso de a WeakReferenceé fazer com que outra classe implemente uma interface. Isso é feito no padrão de ouvinte / observador .

Exemplo prático

Suragch
fonte
explicação confusa. o que é // allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); }??
likejudo
@likejudo, você está certo. Eu melhorei alguns nomes de variáveis ​​e métodos. Como está agora?
Suragch 4/11/19
você precisa verificar o weakreferencepróprio objeto na doSomethingfunção para não estar nullantes de chamar a getfunção.
Behrouz.M
7

Um mapeamento "canonizado" é o local onde você mantém uma instância do objeto em questão na memória e todas as outras pesquisam essa instância em particular por meio de ponteiros ou de algum mecanismo desse tipo. É aqui que as referências fracas podem ajudar. A resposta curta é que WeakReference objetos podem ser usados ​​para criar ponteiros para objetos em seu sistema, enquanto ainda permitem que esses objetos sejam recuperados pelo coletor de lixo depois que eles saem do escopo. Por exemplo, se eu tivesse um código como este:

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

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

Qualquer objeto que eu registrar nunca será recuperado pelo GC porque há uma referência a ele armazenado no conjunto de registeredObjects . Por outro lado, se eu fizer isso:

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

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

Então, quando o GC quiser recuperar os objetos no Conjunto, poderá fazê-lo. Você pode usar esta técnica para armazenar em cache, catalogar etc. Veja abaixo as referências a discussões muito mais aprofundadas sobre GC e armazenamento em cache.

Ref: Coletor de Lixo e WeakReference

Akshay
fonte