Android: Bagaimana cara kerja Bitmap recycle ()?

89

Katakanlah saya telah memuat gambar dalam objek bitmap seperti

Bitmap myBitmap = BitmapFactory.decodeFile(myFile);

Sekarang, apa yang akan terjadi jika saya memuat bitmap lain seperti

myBitmap = BitmapFactory.decodeFile(myFile2);

Apa yang terjadi pada myBitmap pertama? Apakah itu mengumpulkan Sampah atau saya harus mengumpulkan sampah secara manual sebelum memuat bitmap lain, mis. myBitmap.recycle()?

Selain itu, adakah cara yang lebih baik untuk memuat gambar besar dan menampilkannya satu per satu saat mendaur ulang dalam perjalanan?

Anuj Tenani
sumber

Jawaban:

24

Saya pikir masalahnya adalah ini: Pada Android versi pra-Honeycomb, data bitmap mentah sebenarnya tidak disimpan dalam memori VM tetapi dalam memori asli. Memori native ini dibebaskan saat Bitmapobjek java terkait di- GC.

Namun , saat Anda kehabisan memori native, GC dalvik tidak akan terpicu, jadi mungkin saja aplikasi Anda menggunakan sangat sedikit memori java, sehingga dalvik GC tidak pernah dipanggil, namun menggunakan banyak memori native untuk bitmap yang akhirnya menyebabkan kesalahan OOM.

Setidaknya itulah tebakan saya. Untungnya di Honeycomb dan yang lebih baru, semua data bitmap disimpan di VM sehingga Anda tidak perlu menggunakannya recycle()sama sekali. Tapi untuk jutaan pengguna 2,3 (fragmentasi getar tinju ), Anda harus menggunakan recycle()sedapat mungkin (kerumitan besar). Atau sebagai gantinya, Anda mungkin dapat menjalankan GC.

Timmmm
sumber
21

Anda perlu memanggil myBitmap.recycle () sebelum memuat gambar berikutnya.

Bergantung pada sumber myFile Anda (Misalnya, jika itu adalah sesuatu yang Anda tidak memiliki kendali atas ukuran aslinya), saat memuat gambar alih-alih hanya mencontoh ulang beberapa nomor acak, Anda harus menskalakan gambar ke ukuran tampilan.

if (myBitmap != null) {
    myBitmap.recycle();
    myBitmap = null;
}
Bitmap original = BitmapFactory.decodeFile(myFile);
myBitmap = Bitmap.createScaledBitmap(original, displayWidth, displayHeight, true);
if (original != myBitmap)
    original.recycle();
original = null;

Saya menyimpan cache displayWidth & displayHeight dalam keadaan statis yang saya inisialisasi pada awal Aktivitas saya.

Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
djunod.dll
sumber
3
Anda tidak perlu memanggil recycle (), ada baiknya jika Anda ingin segera mengosongkan memori.
Karu
13
Jawaban yang diterima mengatakan "Jika Anda ingin mengosongkan memori ASAP, Anda harus memanggil recycle ()". Jawaban Anda mengatakan "Anda perlu memanggil myBitmap.recycle ()". Ada perbedaan antara "harus" dan "perlu", dan yang terakhir salah dalam kasus ini.
Karu
1
Konteks itu penting. Pertanyaannya adalah "Apakah juga ada cara yang lebih baik untuk memuat gambar besar dan menampilkannya satu per satu untuk didaur ulang dalam perjalanan".
djunod
3
Mulai Android 4.1, contoh di atas mungkin rusak karena dalam beberapa kasus createScaledBitmap dapat mengembalikan instance yang sama dengan yang asli. Itu berarti Anda harus memeriksa yang asli! = MyBitmap sebelum mendaur ulang aslinya.
Jeremyfa
1
@Jeremyfa Ini hanya mengembalikan gambar asli jika Anda menentukan lebar & tinggi yang identik dengan aslinya. Dalam hal ini, penskalaan diperdebatkan, jadi mungkin juga menyimpan beberapa proses dengan melewatinya dan mengembalikan gambar asli sebagai gantinya. Seharusnya tidak "merusak" apa pun ...
Jabari
11

Setelah bitmap dimuat di memori, sebenarnya bitmap dibuat oleh dua bagian data. Bagian pertama berisi informasi tentang bitmap, bagian lain berisi informasi tentang piksel bitmap (dibuat oleh byte array). Bagian pertama ada di Java used memory, bagian kedua ada di C ++ used memory. Itu dapat menggunakan memori satu sama lain secara langsung. Bitmap.recycle () digunakan untuk membebaskan memori C ++. Jika Anda hanya melakukan itu, GC akan mengumpulkan bagian dari java dan memori C selalu digunakan.

Allen
sumber
+1 untuk cara yang menarik tapi sangat bagus untuk menjelaskan mengapa memori tidak tersedia untuk GC langsung - bagus.
Richard Le Mesurier