Dalam pemrograman Android pada umumnya, Handler sering digunakan saat melakukan operasi asinkron dan memproses hasil yang dikembalikan. Biasanya kode kita diimplementasikan seperti ini.
Copy kode kodenya sebagai berikut:
kelas publik SampleActivity memperluas Aktivitas {
Handler akhir pribadi mLeakyHandler = Handler baru() {
@Mengesampingkan
public void handleMessage(Pesan pesan) {
// ...
}
}
}
Namun nyatanya, kode di atas dapat menyebabkan kebocoran memori. Saat Anda menggunakan alat lint Android, Anda akan mendapatkan peringatan seperti itu.
Copy kode kodenya sebagai berikut:
Di Android, kelas Handler harus statis atau kebocoran mungkin terjadi, Pesan yang diantrekan di MessageQueue thread aplikasi juga mempertahankan Handler targetnya. Jika Handler adalah kelas dalam, kelas luarnya juga akan dipertahankan untuk menghindari kebocoran kelas luar. mendeklarasikan Handler sebagai kelas bersarang statis dengan WeakReference ke kelas luarnya
Melihat hal tersebut, Anda mungkin masih bingung. Bagian mana pada kode yang dapat menyebabkan kebocoran memori, dan bagaimana hal tersebut dapat menyebabkan kebocoran memori? Kalau begitu mari kita analisa secara perlahan.
1. Saat aplikasi Android dimulai, instance Looper secara otomatis dibuat untuk digunakan oleh thread utama aplikasi. Tugas utama Looper adalah memproses objek pesan dalam antrian pesan satu per satu. Di Android, semua peristiwa kerangka Android (seperti panggilan metode siklus hidup aktivitas dan klik tombol, dll.) dimasukkan ke dalam pesan, lalu ditambahkan ke antrean pesan untuk diproses oleh Looper, dan Looper bertanggung jawab untuk memprosesnya satu per satu. per satu. Siklus hidup Looper di thread utama sama panjangnya dengan aplikasi saat ini.
2. Ketika Handler diinisialisasi di thread utama dan kami mengirim pesan yang menargetkan Handler ini ke antrian pesan yang diproses oleh Looper, pesan sebenarnya yang dikirim sudah berisi referensi ke instance Handler. Hanya dengan cara ini Looper hanya memproses saat ini pesan diterima dapatkah Handler#handleMessage(Pesan) dipanggil untuk menyelesaikan pemrosesan pesan yang benar.
3. Di Java, kelas dalam non-statis dan kelas dalam anonim secara implisit menyimpan referensi ke kelas luarnya. Kelas dalam statis tidak memiliki referensi ke kelas luar. Untuk informasi lebih lanjut tentang ini, silakan lihat Obrolan Java: Pengubah pribadi "Tidak valid".
Memang benar contoh kode di atas agak sulit untuk mendeteksi kebocoran memori, maka contoh berikut ini sangat jelas
Copy kode kodenya sebagai berikut:
kelas publik SampleActivity memperluas Aktivitas {
Handler akhir pribadi mLeakyHandler = Handler baru() {
@Mengesampingkan
public void handleMessage(Pesan pesan) {
// ...
}
}
@Mengesampingkan
protected void onCreate(Paket yang disimpanInstanceState) {
super.onCreate(savedInstanceState);
// Posting pesan dan tunda eksekusinya selama 10 menit.
mLeakyHandler.postDelayed(Runnable baru() {
@Mengesampingkan
menjalankan kekosongan publik() { /* ... */ }
}, 1000*60*10);
// Kembali ke Aktivitas sebelumnya.
menyelesaikan();
}
}
Menganalisis kode di atas, ketika kita menjalankan metode selesai aktivitas, pesan tertunda akan ada di antrian pesan thread utama selama 10 menit sebelum diproses, dan pesan ini berisi referensi ke Handler, dan Handler adalah anonim. kelas dalam menyimpan referensi ke SampleActivity eksternal, sehingga hal ini menyebabkan SampleActivity tidak dapat didaur ulang. Akibatnya, banyak sumber daya yang disimpan oleh SampleActivity tidak dapat didaur ulang. Ini adalah apa yang sering kita sebut sebagai kebocoran memori.
Perhatikan bahwa Runnable baru di atas juga diimplementasikan oleh kelas dalam anonim. Ia juga menyimpan referensi ke SampleActivity dan mencegah SampleActivity didaur ulang.
Untuk mengatasi masalah ini, idenya adalah untuk tidak menggunakan kelas dalam non-statis. Saat mewarisi Handler, letakkan di file kelas terpisah atau gunakan kelas dalam statis. Karena kelas dalam yang statis tidak memiliki referensi ke kelas luar, hal ini tidak akan menyebabkan kebocoran memori pada instance kelas luar. Saat Anda perlu memanggil Aktivitas eksternal di kelas dalam statis, kita bisa menggunakan referensi lemah untuk menanganinya. Selain itu, Runnable juga perlu ditetapkan sebagai atribut anggota statis. Catatan: Contoh kelas dalam anonim statis tidak memiliki referensi ke kelas luar. Kode yang tidak akan menyebabkan kebocoran memori setelah modifikasi adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
kelas publik SampleActivity memperluas Aktivitas {
/**
* Contoh kelas dalam statis tidak bersifat implisit
* referensi ke kelas luar mereka.
*/
kelas statis pribadi MyHandler extends Handler {
WeakReference akhir pribadi<SampleActivity> mActivity;
public MyHandler(Aktivitas SampleActivity) {
mActivity = WeakReference baru<SampleActivity>(aktivitas);
}
@Mengesampingkan
public void handleMessage(Pesan pesan) {
Aktivitas SampleActivity = mActivity.get();
jika (aktivitas != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(ini);
/**
* Contoh kelas anonim tidak memiliki implisit
* referensi ke kelas luarnya saat "statis".
*/
Runnable akhir statis pribadi sRunnable = Runnable baru() {
@Mengesampingkan
menjalankan kekosongan publik() { /* ... */ }
};
@Mengesampingkan
protected void onCreate(Paket yang disimpanInstanceState) {
super.onCreate(savedInstanceState);
// Posting pesan dan tunda eksekusinya selama 10 menit.
mHandler.postDelayed(sRunnable, 1000*60*10);
// Kembali ke Aktivitas sebelumnya.
menyelesaikan();
}
}
Faktanya, banyak kebocoran memori di Android disebabkan oleh penggunaan kelas internal non-statis di Aktivitas, seperti yang disebutkan dalam artikel ini, jadi kita harus memberikan perhatian khusus saat menggunakan kelas internal non-statis jika contohnya adalah siklus hidup objek yang dipegang lebih besar dari objek kelas luarnya, hal ini dapat menyebabkan kebocoran memori. Secara pribadi, saya cenderung menggunakan kelas artikel statis dan metode referensi lemah untuk menyelesaikan masalah ini.