Catatan: Ada beberapa library yang mengikuti praktik terbaik untuk memuat gambar. Anda dapat menggunakan library tersebut di aplikasi untuk memuat gambar dengan cara yang paling optimal. Kami merekomendasikan library Glide, yang memuat dan menampilkan gambar secepat dan selancar mungkin. Library pemuatan gambar populer lainnya mencakup Picasso dari Square, Coil dari Instacart, dan Fresco dari Facebook. Semua library ini menyederhanakan sebagian besar tugas kompleks yang terkait dengan bitmap dan jenis gambar lainnya di Android.
Gambar tersedia dalam berbagai bentuk dan ukuran. Dalam banyak kasus, gambar lebih besar daripada yang diperlukan untuk antarmuka pengguna (UI) aplikasi biasa. Misalnya, aplikasi Galeri sistem menampilkan foto yang diambil menggunakan kamera perangkat Android yang biasanya beresolusi lebih tinggi daripada kepadatan layar perangkat.
Mengingat bahwa Anda bekerja dengan memori terbatas, idealnya Anda hanya ingin memuat versi resolusi yang lebih rendah dalam memori. Versi resolusi lebih rendah harus cocok dengan ukuran komponen UI yang menampilkannya. Gambar dengan resolusi lebih tinggi tidak memberikan manfaat yang mencolok, tetapi tetap menghabiskan memori yang berharga dan menimbulkan overhead performa tambahan karena penskalaan tambahan saat proses berjalan.
Pelajaran ini memandu Anda mendekode bitmap besar tanpa melebihi batas memori per aplikasi dengan memuat versi subsampel yang lebih kecil dalam memori.
Membaca Dimensi dan Jenis Bitmap
Class BitmapFactory
menyediakan beberapa metode dekode (decodeByteArray()
, decodeFile()
, decodeResource()
, dll.) untuk membuat Bitmap
dari berbagai sumber. Pilih
metode dekode yang paling sesuai berdasarkan sumber data gambar. Metode ini berupaya
mengalokasikan memori untuk bitmap yang dikonstruksi dan, oleh karena itu, dapat dengan mudah menghasilkan pengecualian
OutOfMemory
. Tiap jenis metode dekode memiliki tanda tangan tambahan yang memungkinkan Anda menentukan opsi
dekode melalui class BitmapFactory.Options
. Menetapkan properti inJustDecodeBounds
ke nilai true
saat mendekode
akan mencegah alokasi memori, menampilkan null
untuk objek bitmap, tetapi menetapkan outWidth
, outHeight
, dan outMimeType
. Teknik ini memungkinkan Anda membaca
dimensi dan jenis data gambar sebelum konstruksi (dan alokasi memori)
bitmap.
Kotlin
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } BitmapFactory.decodeResource(resources, R.id.myimage, options) val imageHeight: Int = options.outHeight val imageWidth: Int = options.outWidth val imageType: String = options.outMimeType
Java
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.id.myimage, options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType;
Untuk mencegah pengecualian java.lang.OutOfMemory
, periksa dimensi bitmap sebelum
mendekodenya, kecuali Anda benar-benar memercayai sumber untuk memberi Anda data gambar dengan ukuran yang dapat diprediksi,
yang sangat sesuai dengan memori yang tersedia.
Memuat Versi yang Diperkecil ke Memori
Setelah dimensi gambar diketahui, dimensi dapat digunakan untuk menentukan apakah gambar penuh harus dimuat ke memori atau apakah versi subsampel harus dimuat sebagai gantinya. Berikut ini beberapa faktor yang perlu dipertimbangkan:
- Perkiraan penggunaan memori untuk memuat gambar penuh dalam memori.
- Jumlah memori yang ingin Anda alokasikan untuk memuat gambar ini dengan mempertimbangkan kebutuhan memori lainnya pada aplikasi.
- Dimensi
ImageView
target atau komponen UI yang menerima gambar yang dimuat. - Ukuran dan kepadatan layar perangkat saat ini.
Misalnya, gambar piksel 1024x768 tidak layak dimuat ke memori jika pada akhirnya akan
ditampilkan dalam thumbnail piksel 128x96 di ImageView
.
Untuk memberi tahu decoder agar membuat subsampel gambar, memuat versi yang lebih kecil ke memori, tetapkan inSampleSize
ke true
di objek BitmapFactory.Options
. Misalnya, gambar dengan resolusi 2048x1536 yang
didekode dengan inSampleSize
dari 4 menghasilkan
bitmap berukuran kira-kira 512x384. Pemuatan versi ini ke memori menggunakan 0,75 MB, bukan 12 MB, untuk
gambar penuh (dengan asumsi konfigurasi bitmap ARGB_8888
). Berikut ini
metode untuk menghitung contoh nilai ukuran yang merupakan pangkat dua berdasarkan lebar dan tinggi
target:
Kotlin
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int { // Raw height and width of image val (height: Int, width: Int) = options.run { outHeight to outWidth } var inSampleSize = 1 if (height > reqHeight || width > reqWidth) { val halfHeight: Int = height / 2 val halfWidth: Int = width / 2 // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { inSampleSize *= 2 } } return inSampleSize }
Java
public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
Catatan: Nilai pangkat dua dihitung karena decoder menggunakan
nilai akhir dengan membulatkan ke bawah mendekati pangkat dua terdekat, menurut dokumentasi inSampleSize
.
Untuk menggunakan metode ini, terlebih dulu lakukan dekode dengan inJustDecodeBounds
yang ditetapkan ke true
, teruskan opsi,
lalu dekode lagi menggunakan nilai inSampleSize
yang baru dan inJustDecodeBounds
yang ditetapkan ke false
:
Kotlin
fun decodeSampledBitmapFromResource( res: Resources, resId: Int, reqWidth: Int, reqHeight: Int ): Bitmap { // First decode with inJustDecodeBounds=true to check dimensions return BitmapFactory.Options().run { inJustDecodeBounds = true BitmapFactory.decodeResource(res, resId, this) // Calculate inSampleSize inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight) // Decode bitmap with inSampleSize set inJustDecodeBounds = false BitmapFactory.decodeResource(res, resId, this) } }
Java
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }
Metode ini memudahkan pemuatan bitmap berukuran besar bebas ke ImageView
yang menampilkan thumbnail piksel 100x100, seperti ditunjukkan dalam contoh kode
berikut:
Kotlin
imageView.setImageBitmap( decodeSampledBitmapFromResource(resources, R.id.myimage, 100, 100) )
Java
imageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Anda dapat mengikuti proses serupa untuk mendekode bitmap dari sumber lain, dengan menggantikan
metode BitmapFactory.decode*
yang sesuai menurut kebutuhan.