Bagaimana cara menggunakan WeakReference dalam pengembangan Java dan Android?

166

Saya telah menjadi pengembang java selama 2 tahun.

Tapi saya belum pernah menulis Referensi Lemah dalam kode saya. Bagaimana cara menggunakan WeakReference untuk membuat aplikasi saya lebih efisien terutama aplikasi Android?

Chris
sumber
Mungkin ada perbedaan untuk Android: stackoverflow.com/questions/299659/…
Pacerier

Jawaban:

229

Menggunakan WeakReferencedi Android tidak ada bedanya dengan menggunakannya di Jawa kuno. Berikut ini adalah panduan hebat yang memberikan penjelasan terperinci: Memahami Referensi Lemah .

Anda harus berpikir tentang menggunakan satu setiap kali Anda memerlukan referensi ke suatu objek, tetapi Anda tidak ingin referensi itu untuk melindungi objek dari pengumpul sampah. Contoh klasik adalah cache yang Anda inginkan menjadi sampah yang dikumpulkan ketika penggunaan memori terlalu tinggi (sering diimplementasikan dengan WeakHashMap).

Pastikan untuk memeriksa SoftReferencedan PhantomReferencejuga.

EDIT: Tom telah menimbulkan beberapa kekhawatiran tentang penerapan cache WeakHashMap. Berikut ini adalah artikel yang menjelaskan masalahnya: WeakHashMap bukan cache!

Tom benar bahwa ada keluhan tentang kinerja Netbeans yang buruk karena WeakHashMapcaching.

Saya masih berpikir itu akan menjadi pengalaman belajar yang baik untuk mengimplementasikan cache WeakHashMapdan kemudian membandingkannya dengan cache linting tangan Anda yang diimplementasikan SoftReference. Di dunia nyata, Anda mungkin tidak akan menggunakan salah satu dari solusi ini, karena lebih masuk akal untuk menggunakan perpustakaan pihak ke-3 seperti Apache JCS .

dbyrne
sumber
15
Tidak! Tidak! Tidak! WeakHashMapdigunakan sebagai cache fatal. Entri dapat dihapus segera setelah dibuat. Ini mungkin tidak akan terjadi ketika Anda menguji, tetapi mungkin baik saat digunakan. Sebagai catatan, NetBeans dapat dibawa ke CPU 100% yang efektif dengan ini.
Tom Hawtin - tackline
3
@ Tom, saya sudah memperbarui jawaban saya. Agar adil, saya secara teknis benar bahwa cache sering diterapkan dengan WeakHashMapbahkan jika Anda benar bahwa itu adalah pilihan yang buruk;)
dbyrne
1
Jawaban luar biasa dari dbyrne. Terima kasih untuk ini. Saya tidak melihat alasan mengapa chris tidak menerima jawaban ini.
san
@dbyrne Saya menggunakan objek di Aktivitas saya seperti GridView, ImageView atau BaseAdapter. Dalam metode onDestroy, ketika saya menyelesaikan aktivitas, apakah saya perlu melakukan sesuatu dengan objek ini menggunakan Weak / SoftReferences? Atau sistem membersihkan secara otomatis memori dari objek ini?
beni
4
broken link to java.net
Suragch
64

[EDIT2] Saya menemukan contoh bagus lain dari WeakReference. Memproses Bitmap Dari halaman Thread UI dalam Menampilkan Bitmap Panduan pelatihan yang efisien , menunjukkan satu penggunaan WeakReferencedalam 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);
            }
        }
    }
}

Ia mengatakan,

WeakReference to ImageView memastikan bahwa AsyncTask tidak mencegah ImageView dan referensi apa pun dari pengumpulan sampah . Tidak ada jaminan bahwa ImageView masih ada ketika tugas selesai, jadi Anda juga harus memeriksa referensi di onPostExecute (). ImageView mungkin tidak ada lagi, jika misalnya, pengguna menavigasi menjauh dari aktivitas atau jika perubahan konfigurasi terjadi sebelum tugas selesai.

Selamat coding!


[EDIT] Saya menemukan contoh yang sangat bagus WeakReferencedari facebook-android-SDK . Kelas ToolTipPopup tidak lain adalah kelas widget sederhana yang menunjukkan tooltip di atas tampilan jangkar. Saya menangkap tangkapan layar.

tangkapan layar yang luar biasa

Kelasnya sangat sederhana (sekitar 200 baris) dan layak untuk dilihat. Di kelas itu, WeakReferencekelas digunakan untuk memegang referensi ke tampilan jangkar, yang masuk akal, karena memungkinkan untuk tampilan jangkar menjadi sampah yang dikumpulkan bahkan ketika instance tooltip hidup lebih lama daripada tampilan jangkar.

Selamat coding! :)


Izinkan saya membagikan satu contoh kerja WeakReferencekelas. Ini sedikit cuplikan kode dari widget kerangka Android yang disebut AutoCompleteTextView.

Singkatnya, WeakReference kelas digunakan untuk memegang View objek untuk mencegah kebocoran memori dalam contoh ini.

Saya hanya akan menyalin dan menempelkan kelas PopupDataSetObserver, yang merupakan kelas bersarang AutoCompleteTextView. Ini sangat sederhana dan komentarnya menjelaskan kelas dengan baik. Selamat coding! :)

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

Dan PopupDataSetObserverdigunakan dalam pengaturan adaptor.

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

Satu hal terakhir. Saya juga ingin tahu contoh kerja WeakReferencedalam aplikasi Android, dan saya bisa menemukan beberapa sampel dalam aplikasi sampel resminya. Tetapi saya benar-benar tidak dapat memahami penggunaan beberapa dari mereka. Sebagai contoh, aplikasi ThreadSample dan DisplayingBitmaps digunakan WeakReferencedalam kodenya, tetapi setelah menjalankan beberapa tes, saya menemukan bahwa metode get () tidak pernah kembali null, karena objek tampilan yang direferensikan didaur ulang dalam adaptor, bukan sampah yang dikumpulkan.

김준호
sumber
1
Terima kasih untuk contoh-contoh hebat - Saya benar-benar merasa bahwa ini harus menjadi jawaban yang diterima untuk pertanyaan itu. Bersulang!
Ackshaey Singh
@AckshaeySingh Terima kasih! :)
김준호
16

Beberapa jawaban lain tampaknya tidak lengkap atau terlalu panjang. Inilah jawaban umum.

Cara menggunakan WeakReference di Java dan Android

Anda dapat melakukan langkah-langkah berikut:

  1. Buat WeakReferencevariabel
  2. Tetapkan referensi yang lemah
  3. Gunakan referensi yang lemah

Kode

MyClassmemiliki referensi yang lemah untuk 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
    }

}

AnotherClassmemiliki referensi yang kuat untuk 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);
    }
}

Catatan

  • Alasan Anda membutuhkan referensi yang lemah adalah agar Pengumpul Sampah dapat membuang benda-benda itu ketika mereka tidak lagi dibutuhkan. Jika dua objek mempertahankan referensi kuat satu sama lain, maka mereka tidak dapat dikumpulkan sebagai sampah. Ini adalah kebocoran memori.
  • Jika dua objek perlu referensi satu sama lain, objek A (umumnya objek yang berumur pendek) harus memiliki referensi yang lemah ke objek B (umumnya objek yang berumur lebih panjang), sedangkan B memiliki referensi yang kuat untuk A. Pada contoh di atas, MyClassadalah A dan AnotherClassB.
  • Alternatif untuk menggunakan a WeakReferenceadalah memiliki kelas lain mengimplementasikan antarmuka. Ini dilakukan dalam Pola Pendengar / Pengamat .

Contoh praktis

Suragch
sumber
penjelasan yang membingungkan. apa itu // allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); }??
likejudo
@likejudo, Anda benar. Saya meningkatkan beberapa penamaan variabel dan metode. Bagaimana sekarang?
Suragch
Anda perlu memeriksa weakreferenceobjek itu sendiri doSomethingagar tidak berfungsi nullsebelum memanggil getfungsi.
Behrouz.
7

Pemetaan "dikanonik" adalah di mana Anda menyimpan satu instance objek yang bersangkutan dalam memori dan semua yang lain mencari contoh tertentu melalui pointer atau mekanisme semacam itu. Di sinilah referensi lemah dapat membantu. Jawaban singkatnya adalah bahwa objek WeakReference dapat digunakan untuk membuat pointer ke objek di sistem Anda sambil tetap membiarkan objek-objek tersebut untuk direklamasi oleh pengumpul sampah begitu mereka keluar dari ruang lingkup. Sebagai contoh jika saya memiliki kode seperti ini:

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

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

Objek apa pun yang saya daftarkan tidak akan pernah direklamasi oleh GC karena ada referensi untuk itu disimpan dalam set registeredObjects. Di sisi lain jika saya melakukan ini:

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

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

Kemudian ketika GC ingin mengklaim kembali objek dalam Set itu akan dapat melakukannya. Anda dapat menggunakan teknik ini untuk caching, katalog, dll. Lihat di bawah untuk referensi untuk diskusi yang lebih mendalam tentang GC dan caching.

Ref: Pengumpul sampah dan Referensi Lemah

Akshay
sumber