Mengelola memori Bitmap sangat penting untuk mengurangi risiko Kehabisan Memori (OOM) di aplikasi Android yang kaya visual. Meskipun developer tahu cara mengurangi ukuran gambar, stabilitas aplikasi jangka panjang dapat dicapai dengan menguasai dua pilar utama lainnya: Penggunaan Kembali dan Daur Ulang. Sesi ini memberikan playbook praktis untuk mengoptimalkan performa memori menggunakan alat modern, library open source seperti Glide dan Coil, serta API tingkat sistem.
Dalam sesi ini, Anda akan mempelajari cara mendiagnosis regresi memori di dunia nyata menggunakan Android Vitals, menerapkan pola caching lanjutan (Reuse) dengan Glide dan Coil, serta merilis memori secara proaktif (Recycle) menggunakan callback onTrimMemory.
BitmapRegionDecoder, bukan memuat gambar beresolusi penuh yang terlalu besar.onTrimMemory. Hentikan polling dan animasi latar belakang di onStop untuk mencegah pertumbuhan memori latar belakang.Transkrip
Fung Lam
Ya, kita punya waktu sekitar 1 jam sebelum makan siang, jadi tetaplah bersama saya.
Saya Fung Lam, developer relations engineer di Android yang berbasis di London.
Ya, terima kasih.
Sebelum saya mulai, bolehkah saya mengajukan pertanyaan?
Berapa banyak dari Anda yang membeli ponsel baru tahun ini?
Wow.
Wow.
Jujur saja, saya tidak akan menyalahkan Anda jika Anda tidak membeli Pixel.
Ya, harganya lebih mahal, kan, untuk ponsel baru.
Ya, itulah alasan beberapa dari Anda tidak membeli ponsel baru.
Mengapa harganya lebih mahal?
Tahukah Anda?
Tidak.
RAM.
RAM, ya, ya.
Ya, RAM adalah tantangan nyata bagi Android dan bagi kami, bagi developer.
Oleh karena itu, hari ini saya akan menjelaskan cara mengatasi tantangan terkait memori, yaitu RAM.
Sebelumnya di Google I/O dan Google I/O Connect, kami mengadakan beberapa sesi tentang memori, mulai dari cara sistem operasi mengalokasikan memori, cara membuat profil aplikasi, cara mengoptimalkan kode menggunakan R8, dan tentu saja, saya membahas sedikit tentang Bitmap pada saat itu.
Anda dapat melihat foto saya di screenshot.
Pengoptimalan memori adalah salah satu domain besar, yang mencakup berbagai area seperti Android runtime, pembersihan sampah memori, alat performa, alokasi memori objek, compiler, dll. Dan dalam sesi ini, dalam 30 menit ini, saya akan memberi Anda beberapa informasi tentang cara mengoptimalkan memori Bitmap.
Anda mungkin bertanya, mengapa, mengapa Bitmap?
Mengapa kita harus berfokus pada memori Bitmap?
Karena satu gambar bernilai lebih dari seribu kata.
Itu benar, saya akan menjelaskan alasannya kepada Anda.
Di layar ponsel, misalnya di Pixel 7, Pixel 9, atau Pixel 11 Pro, resolusi layarnya sekitar 2.500 1.200, 1.400.
Jadi, untuk setiap piksel, kita membawa 32 bit kedalaman warna.
Jadi, untuk gambar layar penuh, oh, jari saya tidak cukup, tetapi, misalnya, akan membutuhkan sekitar 15 MB memori, 50 MB.
Jadi, misalnya, sebuah word dalam sistem OS adalah 64-bit, yang berarti 8 byte.
Seribu kata hanya delapan, 8 MB.
Ya, 8K.
Jadi, satu gambar pasti bernilai lebih dari seribu kata.
Ya, ini penting.
Satu gambar sudah berukuran 15 MB.
Bagaimana jika aplikasi Anda memiliki 10, 100 gambar?
Jumlahnya sangat besar.
Jadi, sebenarnya Bitmap selalu, biasanya merupakan pemakai memori terbesar dalam aplikasi Anda.
Saat gambar didekode ke dalam memori, seperti yang saya sebutkan, gambar tersebut akan diperluas sesuai dimensi dan kedalaman warna.
Ini bukan tentang ukuran file, JPEG, atau format PNG.
Tidak, gambar akan didekode dan menggunakan memori.
Jadi, hanya beberapa gambar beresolusi penuh akan dengan mudah menghabiskan ratusan megabyte memori di perangkat Anda.
Kedua, saat memori sangat terbatas, sistem operasi harus menggunakan lebih banyak daya untuk menangani konsumsi memori.
Hal ini akan menyebabkan UI tampak tersendat-sendat.
Atau, OS juga dapat menghentikan aplikasi Anda untuk mengosongkan memori.
Atau, aplikasi Anda juga akan mengalami error Kehabisan Memori, pengecualian Kehabisan Memori.
Untungnya, tampilan jam ini biasanya sangat disukai dan mudah dibuat.
Sebagian besar lonjakan memori berasal dari kesalahan umum, yang dapat dihindari, dan kita dapat memperbaikinya tanpa mengorbankan kualitas gambar.
Sekarang Anda tahu, memori Bitmap penting untuk aplikasi Anda, tidak hanya untuk membuatnya lancar, tetapi juga untuk stabilitas dan retensi pengguna.
Jadi, hari ini kita akan membahas lima bagian sederhana untuk menjaga aplikasi Anda tetap berjalan lancar.
Pertama, kita akan membahas kualitas teknis Google Play, persyaratan kualitas teknis baru, yang mencakup batas memori mendatang dan tempat Anda dapat memeriksa batas tersebut dengan mudah.
Kedua, saya akan membagikan studi kasus tentang 500 aplikasi Android, cara aplikasi tersebut menggunakan memori, apakah cara yang digunakan sudah benar, atau apakah ada hal yang dapat kita pelajari?
Setelah itu, saya akan menunjukkan prinsip cara mengoptimalkan penggunaan memori Bitmap.
Terakhir, kami akan membagikan beberapa alat yang dapat membantu Anda memeriksa aplikasi Anda sendiri.
Fung Lam
Pertama, persyaratan kualitas baru Google Play.
Seberapa sering Anda mengunjungi Konsol Google Play?
Setiap bulan?
Setiap minggu?
Tidak pernah?
Tertawa itu berarti ya, kan?
Tidak?
Tidak.
Apakah Anda mengunjungi bagian Android Vitals?
Ya, ya.
Ya, jadi Anda pasti tahu sesuatu sebelum saya membuat ini.
Jadi, di Google Play, ada persyaratan kualitas baru terkait nilai minimum performa.
Kami, Android dan Play, ingin pengguna menggunakan aplikasi dengan kualitas yang baik.
Jadi, nilai minimum ini juga merupakan sesuatu yang kami coba berikan yang terbaik kepada pengguna akhir.
Dengan kata lain, "Hei, aplikasi Anda, saat berjalan di latar belakang, atau di platform latar depan yang dapat diamati, maka persentil ke-90 penggunaan memori Anda tidak boleh melebihi batas ini.
Atau, visibilitas aplikasi Anda di Google Play Store mungkin akan berkurang, atau semacamnya.
Penegakan kebijakan ini akan dimulai pada Februari 2027 untuk aplikasi dan game.
Jika Anda melampaui batas ini, penilaian kesehatan aplikasi Anda akan terpengaruh secara langsung.
Jadi, pastikan Anda membuka Konsol Google Play dan memeriksa, "Berapa persentil ke-90 saya hari ini?"
Dan lakukan peningkatan sebelum batas waktu.
Ini adalah animasinya.
"Hei, jika Anda membuka Konsol Google Play, Anda dapat melihat, ya, ada Android vitals.
Ada bagian memori.
Anda juga dapat melihat persentil ke-90.
Dan jika Anda mengkliknya, ya, Anda dapat melihat rata-rata konsumsi memori dari waktu ke waktu.
Ada juga berbagai tingkatan berdasarkan RAM, faktor bentuk, kepadatan layar, versi OS, yang membantu Anda mengisolasi bottleneck memori pada perangkat entry-level tertentu, atau pada rilis aplikasi tertentu.
Sekarang, saya tahu, Anda mungkin berkata, "Hei, terima kasih telah memberi tahu saya bahwa aplikasi saya melebihi batas.
Tapi sungguh, saya tidak tahu apa yang salah.
Maukah Anda membantu?"
Ya, itulah sebabnya saya di sini untuk membantu Anda.
Saya akan mencoba membuatnya mudah bagi Anda, seperti, hei, jika Anda sedang menonton TV, lalu TV Anda tidak memiliki video atau audio, apa yang akan Anda lakukan?
Anda mengeluarkan panduan TV, membuka beberapa halaman terakhir, yang biasanya merupakan panduan pemecahan masalah, yang mengatakan, "Jika TV Anda tidak bersuara, coba tombol ini, atau coba tombol itu."
Ya, sama halnya, hari ini, saya mengidentifikasi beberapa masalah umum.
Semoga panduan ini dapat membantu Anda memecahkan masalah.
Biasanya, saat membaca panduan TV, kita harus mulai dari halaman pertama, yang biasanya berisi toolkit diagnostik untuk membantu kita memahami cara kerja TV.
Namun, karena saya tahu bahwa hal itu kurang menarik, hari ini, izinkan saya membahas pemecahan masalah terlebih dahulu, dan kita akan kembali membahasnya nanti.
Sejujurnya, ini adalah cara saya membaca panduan TV.
Untuk mengungkap masalah memori Bitmap alokasi Bitmap yang paling sering terjadi di ekosistem Android, saya meneliti sekitar 500 aplikasi, aplikasi populer, di Android.
Saya benar-benar menjalankan aplikasi, memulai aplikasi, login, membuka berbagai halaman, menjelajah lebih dalam, dan kembali.
Saya juga menguji aplikasi di berbagai RAM dan perangkat, seperti perangkat Android Go dengan memori 2 GB, beberapa perangkat entry-level 4 GB, beberapa perangkat mid-level 4 GB, 6 GB hingga 8 GB, atau beberapa perangkat foldable premium dengan layar yang sangat besar, kualitas layar yang sangat baik, dan memori 16 GB.
Kami mengukur penggunaan memori di perangkat ini saat berada di status aplikasi yang berbeda, mengambil heap dump, dan menggunakan berbagai alat, termasuk Perfetto AI Skill, untuk menganalisis alokasi, guna mengidentifikasi antipola umum, ya, kesalahan umum yang kita buat, dalam skala besar.
Jadi, inilah cara memori berubah dalam berbagai status aktivitas.
Dengan analisis ini, kami mengidentifikasi lebih dari 15 masalah umum, mulai dari gambar yang terlalu besar, hingga peningkatan cache latar belakang.
Jadi, ini adalah studi kasus inti yang akan kita bagikan dan pelajari hari ini.
Fung Lam
Ya, semua studi kasus yang saya bagikan sekarang adalah tentang riset 500 aplikasi.
Karena Anda berada di sini, saya rasa kita semua menyukai Android, bukan?
Kita semua menyukai Android.
Katakanlah ada salah satu dari kita yang juga menyukai Android, tetapi dia bukan pakar dalam pengembangan Android.
Jadi, dia membuat aplikasi Android untuk memesan figurin Android.
Namun, ada beberapa kesalahan.
Mari kita bahas. Mari kita lihat, mungkin ini kesalahan pertama.
"Hei, di halaman produk, kita punya tata letak dua kolom yang menampilkan berbagai figurin Android.
Untuk setiap kolom, gambar berukuran sekitar 400 x 400 piksel.
Namun, aplikasi sebenarnya mendownload satu gambar dari server, berapa pun ukuran layar saya.
Gambar berukuran 3.000 x 3.000 sedang didownload.
Ya, karena seperti developer, "Hei, Android sangat indah, sangat luar biasa.
Saya ingin menampilkan yang terbaik kepada pengguna."
Namun, sebenarnya tidak, karena, hei, ini dibatasi oleh dimensi tampilan gambar.
Jadi, meskipun Anda mendownload gambar besar, gambar tersebut tidak ditampilkan dengan baik kepada pengguna.
Selain itu, gambar besar ini menyebabkan penggunaan memori tambahan.
Meskipun kita melakukan downsample, proses downsample juga menggunakan memori tambahan.
Bagaimana cara membantu developer ini memperbaikinya?
Jika Anda mengetahui tentang CDN, CDN sebenarnya dapat menangani beberapa operasi gambar, seperti memangkas, mengubah ukuran, memutar, menerapkan filter, mengubah gambar dari persegi menjadi bulat.
Dengan permintaan HTTP, CDN dapat menangani sesuatu.
Jadi, meskipun sisi server Anda hanya menghosting satu gambar, Anda dapat menggunakan CDN untuk mengirim gambar yang diubah ukurannya ke perangkat Anda.
Jadi, nomor 1, ya saat Anda meminta gambar, coba gunakan CDN, sehingga Anda akan meminta gambar sesuai ukuran ImageView.
Namun, jika tidak memungkinkan, misalnya terkadang ukurannya bisa sedikit lebih besar dari ImageView, ingatlah untuk melakukan downsample guna mengubah ukurannya dalam kode.
Jadi, saat dimuat ke dalam memori, gambar yang dimuat adalah gambar yang lebih kecil, bukan gambar dengan resolusi penuh.
Berikut adalah contoh kode untuk Coil.
// 1. Ask CDN for the exact UI card dimensions (e.g. 540x675)
val sizedUrl = "$cdnBaseUrl?w=$cardWidthPx&h=$cardHeightPx&fmt=webp"
// 2. Enforce exact downsampling + Hardware Bitmap config
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(sizedUrl)
.size(cardWidthPx, cardHeightPx) // Never use Size.ORIGINAL!
.precision(Precision.EXACT) // Downsample to exact card size
.bitmapConfig(Bitmap.Config.HARDWARE)
.build(),
contentDescription = product.title
)Anda dapat meneruskan ukuran sebagai kata-kata dalam warna kuning.
Ya, Anda dapat meneruskan ukuran sebagai HTTP dalam permintaan HTTP.
Lalu, di sini, dalam kode yang ditandai, apakah Anda dapat melihat tandanya, ya, ya, jangan gunakan Size.ORIGINAL.
Ya, gunakan lebar dan tinggi, sehingga akan diteruskan sebagai parameter.
Gunakan presisi untuk melakukan penurunan sampel pada gambar ke ukuran yang tepat.
Dan jika memungkinkan, gunakan Bitmap.Config.HARDWARE, yang juga menggunakan lebih sedikit memori karena gambar akan disimpan langsung di VRAM.
Demikian pula, di aplikasi ini, misalnya, mereka merilis versi baru koleksi Android, jadi ada gambar panjang yang dapat digeser pengguna ke kiri dan ke kanan untuk melihat berbagai desain figur Android baru.
Namun, ada juga masalah lain di sini.
Untuk gambar yang panjang, sebagian besar sebenarnya berada di luar layar, yang berarti pengguna tidak dapat melihatnya, tetapi gambar tersebut dimuat dalam memori.
Could we do it better?
Ya.
Kita dapat memotongnya menjadi gambar yang lebih kecil, dan hanya menampilkan gambar yang ditampilkan di layar yang akan dimuat.
Iklan yang berada di luar layar tidak akan dimuat, atau akan dibatalkan pemuatannya.
Jadi, penggunaan memorinya akan lebih sedikit.
Demikian pula, oh, jadi dalam hal ini, model ini menghemat sekitar 60% memori.
Tentu saja, jika Anda memangkasnya menjadi persegi panjang yang lebih kecil, Anda akan menghemat lebih banyak.
Namun, jika pengguna ingin menyediakan fitur bagi pengguna, misalnya, memperbesar dan memperbesar untuk melihat setiap detail patung Android, beberapa kesalahan umum adalah mereka benar-benar memuat, sekali lagi, gambar yang sangat besar, sehingga dapat diperbesar dan diperbesar lagi.
Namun, tidak, Anda tidak perlu melakukannya.
Cara yang lebih baik adalah saat pengguna memperbesar gambar beresolusi rendah yang sudah dimuat, dan saat pengguna berhenti mencubit, Anda menghitung koordinatnya, lalu memuat resolusi penuh-- hanya memuat area tersebut, area yang dihitung dari gambar beresolusi tinggi.
Jadi, hanya area yang terlihat yang akan dimuat ke dalam memori.
Bagaimana cara melakukannya?
Oh, ada API bernama BitmapRegionDecoder, yang membantu Anda memuat region tertentu dari gambar.
Dan tentu saja, ada juga beberapa alat dan library yang dapat membantu Anda.
Anda dapat menelusurinya di GitHub.
Beberapa aplikasi komik atau beberapa peta sudah menggunakan fitur ini.
Anda dapat menggunakannya sebagai referensi.
Selain itu, kita adalah manusia, kita membuat kesalahan.
AI juga membuat kesalahan.
Jadi, untuk mencegah kesalahan, kita juga dapat menambahkan beberapa batasan saat memuat gambar.
Misalnya, dalam kasus ini, saya dapat mengatakan, "Hei, mari kita gunakan ukuran jendela atau dimensi layar sebagai batas dan memasukkannya ke dalam fungsi min, min lebar permintaan atau lebar batas."
// Query active window bounds instead of hardcoding 1080 / 1920
val bounds = WindowMetricsCalculator.getOrCreate()
.computeCurrentWindowMetrics(activity).bounds
// Clamp target dimensions so Bitmap NEVER exceeds the active window
val safeWidth = minOf(requestedWidthPx, bounds.width())
val safeHeight = minOf(requestedHeightPx, bounds.height())
val bitmap = Bitmap.createBitmap(safeWidth, safeHeight, config)Jadi, dengan fungsi ini, setiap kali saya memuat gambar, gambar tersebut tidak akan pernah melebihi batas.
Jadi, gambar yang dimuat tidak akan lebih besar dari resolusi layar.
Ingat, hanya dekode wilayah gambar yang terlihat yang akan menghemat memori Anda.
Fung Lam
Sekali lagi, katakan, hei, aplikasi ini sebenarnya bukan dari satu developer.
Sebenarnya, ini untuk beberapa kontributor dan mereka mungkin menggunakan alat yang berbeda.
Misalnya, sebagian menggunakan, oh maaf.
Saya akan mulai lagi.
Sama seperti aplikasi ini.
Halo, di halaman produk, saya memiliki daftar produk dan saya melihat gambar ini, saya menambahkannya ke keranjang belanja.
Beberapa menit kemudian, saya membuka halaman checkout, dan gambar yang sama muncul lagi.
Namun, apakah ini benar-benar gambar yang sama atau, sebenarnya, ini adalah dua objek yang berbeda dalam memori?
Ya, salah satu kesalahan umum yang kami temukan adalah, aplikasi memuat banyak gambar.
Namun, mereka juga memiliki banyak gambar duplikat di memori.
Jika Anda menggunakan pemuat gambar, ya, sebenarnya, Anda dapat meneruskan kunci untuk melakukan caching.
Jadi, saat gambar yang sama digunakan, Anda dapat menggunakan objek Bitmap yang sudah dialokasikan, bukan membuat objek baru.
Di Android Studio, Anda dapat menggunakan Memory Profiler untuk menemukan, apakah aplikasi saya memiliki Bitmap duplikat?
Saya membahasnya di sesi Google I/O, jadi Anda dapat kembali ke YouTube untuk menonton videonya.
Jika Anda tidak menggunakan Android Studio, ada juga perintah adb untuk mengambil heap dump, lalu Anda dapat menggunakan bahasa SQL grafik heap Perfetto untuk mengkueri apakah aplikasi Anda memiliki Bitmap duplikat.
Jadi, ini adalah contoh kode di Coil, cara menambahkan kunci untuk caching Bitmap.
// Strip volatile query tokens (?from=list / ?sig=...) for cache key
val canonicalKey = MemoryCache.Key(productUrl.substringBefore("?"))
// On Checkout Screen: reuse the 200x200 Bitmap already loaded in the List!
AsyncImage(
model = ImageRequest.Builder(context)
.data(productUrl)
.placeholderMemoryCacheKey(canonicalKey) // Instant zero-decode reuse!
.memoryCacheKey(canonicalKey)
.diskCacheKey(productUrl.substringBefore("?"))
.build(),
contentDescription = product.title
)Dan sekarang, ya.
Saya kembali ke contoh sebelumnya.
Halo, aplikasi saya memiliki developer yang berbeda.
Beberapa di antaranya menggunakan View, jadi ini menggunakan Glide sebagai pemuat gambar.
Beberapa di antaranya menggunakan Coil karena menggunakan Compose.
Jadi, apakah pemuat gambar yang baru saja Anda sebutkan, oh, pemuat gambar membantu kita melakukan caching, jadi apakah ada yang salah jika saya memiliki lebih dari satu pemuat gambar?
Pasti ada yang salah.
Jadi, untuk loader gambar, secara default, loader akan menggunakan X% dari ukuran memori heap sebagai ukuran cache default.
Jadi, jika Anda memiliki satu loader gambar, X% memori Anda akan digunakan.
Jika Anda memiliki dua pemuat gambar, memori yang digunakan akan menjadi 2X lipat.
Jika Anda memiliki tiga pemuat gambar, jumlahnya akan menjadi 3X.
Dan jika loader gambar Anda tidak menggunakan kumpulan yang sama, hal yang sama akan terjadi.
Seperti di halaman produk, saya memuat gambar menggunakan Glide, lalu di halaman checkout, gambar yang sama akan dimuat menggunakan Coil.
Namun, akan ada dua gambar dalam kenangan.
Itu tidak baik.
Ya, jadi untuk memperbaikinya, tentu saja, idealnya, kita hanya menggunakan satu pemuat gambar atau satu kumpulan bersama untuk penyimpanan cache.
Namun, bagaimana jika ini seperti, oh, banyak pekerjaan engineering, saya tidak bisa melakukannya.
Oh, perbaikan yang lebih mudah adalah, hei, saya mengukur berapa persen gambar yang akan ditangani oleh pemuat gambar ini, berapa persen gambar yang akan ditangani oleh pemuat gambar lain, dan menetapkan bobot untuk pemuat gambar yang berbeda, seperti kode ini.
val totalBudgetBytes = computeAdaptiveCacheBytes(context)
val numberOfLoaders = 2 // e.g., Team A (Glide) + Team B (Coil)
// Approach A (With Stats): Weight by image traffic (70% Team A / 30% Team B)
val loaderABudget = (totalBudgetBytes * 0.70).toInt()
val loaderBBudget = (totalBudgetBytes * 0.30).toInt()
// Approach B (Simple Fix without stats): Divide by number of loaders!
val simpleSplitBudget = totalBudgetBytes / numberOfLoaders
MemoryCache.Builder()
.maxSizeBytes(simpleSplitBudget)
.build()Saya dapat menetapkan loaderABudget menjadi 70%, loaderBBudget menjadi 30%, jadi itu cara lain.
Jika Anda benar-benar tidak tahu berapa banyak gambar yang akan ditangani oleh setiap loader, sebaiknya bagi saja.
Hei, saya punya dua pemuat gambar, bagi dengan 2; tiga pemuat gambar, bagi dengan 3, daripada menggunakan nilai default karena itu akan menggunakan memori secara berlebihan.
Dan katakanlah, di halaman produk, kita berharap pengguna men-scroll untuk melihat lebih banyak produk.
Jadi biasanya, kita mungkin akan menyimpan cache sekitar 50 gambar atau 50 MB gambar.
Namun, bagaimana jika aplikasi beralih ke latar belakang?
Oh, jika aplikasi berada di latar belakang, pengguna mungkin tidak sedang menjelajah.
Jika pengguna tidak menjelajah, apakah saya harus tetap menyimpan jumlah gambar cache ini di memori?
Tidak, kita harus merilisnya.
Ya, kita harus mematuhi callback onTrimMemory.
Ya, sebenarnya banyak aplikasi yang saat ini tidak menerapkan atau tidak mematuhi callback onTrimMemory.
Untuk callback onTrimMemory, ada dua sinyal yang harus kita perhatikan.
Salah satunya adalah UI_HIDDEN, yang seperti pengguna telah menekan tombol layar utama, dan ini tidak langsung setelah mereka menekan tombol layar utama, melainkan jangka waktu setelah mereka menekan tombol Layar utama, yang berarti, "Oh, aplikasi Anda sebenarnya dalam status tidak ada UI yang terlihat oleh pengguna."
Dan kita bisa menduga, "Hei, pengguna tidak menggunakan aplikasi Anda.
Anda harus merilis resource ke OS."
Jadi, kita harus memangkas ukuran cache.
Dan yang lainnya adalah TRIM_MEMORY_BACKGROUND, yang berarti aplikasi tersebut sudah lama berada di latar belakang, dan sekarang OS benar-benar membutuhkan beberapa resource.
Pada tahap ini, Anda harus melepaskan memori sebanyak mungkin, hampir 100%.
Dengan melakukan ini, Anda juga mengurangi kemungkinan dan risiko aplikasi dihentikan oleh OS.
Karena, "Hei, mereka ingin memori, Anda memberikannya, lalu OS tersebut akan menanganinya".
Jika tidak, OS tidak punya pilihan, OS harus melakukan sesuatu.
Fung Lam
Jadi, kesalahan besar lain yang saya lihat adalah, "Hei, saya baru saja menyebutkan bahwa aplikasi Anda, jika aplikasi, jika berjalan di latar belakang, seharusnya menggunakan lebih sedikit memori."
Namun, saya melihat aplikasi benar-benar menggunakan lebih banyak memori di latar belakang, lebih banyak memori.
Mengapa?
Itulah sebabnya saya melakukan riset.
Saya mencoba mencari tahu alasannya.
Salah satu alasannya adalah, "Hei, di aplikasi mereka, ada animasi atau carousel.
Misalnya, aplikasi tersebut mendownload gambar baru setiap 15 detik, dan menampilkan gambar baru kepada pengguna."
Namun, developer tidak menghentikan animasi saat aplikasi berjalan di latar belakang.
Jadi, meskipun aplikasi berada di latar belakang, kode masih berjalan, masih mendapatkan gambar baru, mendekode gambar baru, dan menggunakan lebih banyak memori.
Jadi, ini sebenarnya adalah sesuatu yang tidak boleh dilakukan aplikasi.
Yap.
Kita harus menghentikan semua animasi, caching, atau pekerjaan yang tidak terkait saat aplikasi berjalan di latar belakang.
Jadi, jika Anda mengetahui siklus proses Activity, ada peristiwa onPause, onStop, pastikan Anda mengimplementasikan onStop, onPause, onDestroyView untuk menghentikan animasi, ya, untuk menghentikan permintaan pull gambar, atau hal lain yang terkait dengan pekerjaan yang tidak memerlukan perhatian pengguna.
override fun onStop() {
super.onStop()
// 1. Stop animations so they don't decode next frames in background!
bannerAnimationView.cancelAnimation()
animatedImageDrawable?.stop()
// 2. Cancel next-image buffering / prefetch queues!
nextImagesBufferingJob?.cancel()
Glide.with(this).pauseRequests()
}
override fun onDestroyView() {
super.onDestroyView()
bannerAnimationView.clearAnimation() // Release resources!
}Fung Lam
Saya tahu saya telah membahas banyak kasus dengan Anda, tetapi saya berkata, "Saya memiliki lebih dari 15 kasus."
Saya tahu jika saya membagikan semua kasus kepada Anda, Anda mungkin akan melupakan semuanya.
Anda hanya akan kembali saat pulang ke rumah, Anda hanya akan menelusuri Google, "Hei, bagaimana cara memperbaiki masalah memori saya?"
Jadi, saya akan memudahkan Anda untuk mengingatnya.
Yap.
Saya akan mengaitkannya dengan framework 3R yang sangat terkenal untuk membantu Anda mengingatnya.
Saya rasa semua orang pasti pernah mendengar framework 3R ini.
Yang pertama adalah mengurangi, cara mengurangi memori saat memuat gambar.
Tentu saja, seperti yang saya sebutkan, kita dapat melakukan downsample, mendapatkan dimensi gambar yang tepat saat kita mendapatkannya dari server.
Kami mengoptimalkan kedalaman warna, atau menggunakan Bitmap hardware jika memungkinkan.
Jika Anda tidak menggunakan CDN, coba gunakan CDN.
Jadi, saat kita mendapatkan gambar dari server, gambar yang didapatkan adalah gambar yang paling sesuai dengan ukuran.
Penggunaan kembali, jadi kita menggunakan cache agar pemuatan gambar ke dalam memori akan digunakan kembali, dan kita tidak memiliki gambar Bitmap duplikat dalam memori.
Dan, tentu saja, cobalah untuk tidak memiliki beberapa loader gambar dalam kode Anda, karena hal itu kurang efisien.
Kurangi, gunakan kembali, dan apa yang tersisa?
Tahukah Anda?
Daur ulang.
Coba lagi?
Daur ulang, ya, daur ulang.
Ya, kita belajar mengurangi, menggunakan kembali, dan mendaur ulang di sekolah dasar, meskipun kita tidak belajar coding di sekolah dasar.
Ya, daur ulang, ya.
Ya, ini tentang cara melepaskan memori sehingga aplikasi lain atau OS dapat menggunakan memori tersebut.
Ada juga hal-hal seperti daur ulang.
Pertama, saya sebutkan ada peristiwa onTrimMemory, misalnya ada dua sinyal yang akan dikirim OS kepada Anda saat UI disembunyikan atau OS benar-benar memerlukan resource.
Ya, coba tangani permintaan ini.
Dan, tentu saja, jika Anda memiliki animasi, pemuat gambar, atau beberapa pekerjaan lain yang terjadi di latar depan, pastikan Anda menghentikannya saat ini seperti pada peristiwa onPause, onStop.
Jadi, kurangi, gunakan kembali, daur ulang.
Mudah diingat, bukan?
Fung Lam
Sekarang kita akan kembali ke bagian yang kurang menarik dari panduan TV.
Bagaimana cara menggunakan berbagai toolkit untuk mendeteksi dan mengukur masalah memori ini secara efektif?
Jadi, yang pertama adalah Konsol Google Play, Android vitals, dan Reporting API.
Ya, periksa persentil ke-90 di Konsol Google Play.
Di mana Anda sekarang?
Atau, gunakan Reporting API jika Anda tidak membuka halaman web.
Anda masih dapat menggunakan API untuk mendapatkan nilai.
Ya, pastikan, ya, batasnya adalah 200 MB dan 400 MB.
Pastikan Anda tidak melebihi nilai minimum.
Ada juga perintah ADB untuk membantu Anda, seperti mengambil heap dump atau mengirim peristiwa untuk menyimulasikan, hei, aplikasi saya sekarang berada dalam tahap UI_HIDDEN.
adb shell dumpsys meminfo <pkg> # watch Native Heap!
adb shell am send-trim-memory <pkg> UI_HIDDENJika Anda menggunakan Android Studio, bagus.
Namun, ini selalu menjadi teman baik untuk Anda.
Anda dapat menggunakan profiler Android Studio untuk merekam heap dump, agar memiliki UI untuk melihat penggunaan memori dan alokasi memori yang sebenarnya.
Dan tentu saja, kita akan membahas AI dengan Gemini di Android Studio atau keterampilan Android lainnya yang tersedia.
Alat ini membantu Anda menganalisis heap dump dan menggunakan rekaman aktivitas SQL Perfetto untuk membantu Anda menemukan Bitmap, yang biasanya memberi tahu siapa pemilik cache.
Apakah gambar saya dimuat oleh Glide atau dimuat oleh Coil?
Apakah ada kebocoran memori lain di aplikasi saya?
Saya tahu beberapa dari Anda berkata, "Hei, sebenarnya saya tidak menggunakan IDE lagi.
Saya adalah engineer perintah.
Saya menggunakan, ya, saya hanya mengirim perintah dengan agen AI untuk membuat kode."
Ya, saya tahu.
Saya juga melakukan vibe coding di waktu luang.
Jadi, saya membagikan ini kepada Anda hanya sebagai contoh.
Ya, ini hanya bagian dari perintah yang saya gunakan untuk riset 500 aplikasi.
Ya, hanya sebagian.
Anda tahu bahwa layarnya tidak besar.
Jika saya menambahkan lebih banyak, Anda hampir tidak dapat melihatnya.
Namun, hal ini memberi Anda gambaran tentang cara menggunakan AI untuk mengotomatiskan, menangkap penggunaan memori, termasuk heap dump, serta menganalisis dan memberikan temuan kepada Anda.
Dan ya, saya bisa mengatakan di sini, dalam satu kalimat lagi, ya, "Berikan juga perbaikan kode untuk kode saya."
Ya, saya harap ini bisa mengurangi ketakutan Anda, katakanlah, "Hei, memori adalah domain yang besar.
Saya tidak mempelajari sistem operasi atau arsitektur sistem komputer dalam ilmu komputer di universitas saya.
Tapi sebenarnya, ini tidak terlalu menakutkan, ini tidak terlalu sulit."
Ya, ini memudahkan Anda menganalisis aplikasi.
Coba, yuk.
Namun, tentu saja AI akan membuat kesalahan, sehingga pengetahuan dasar tentang penggunaan memori dan cara OS mengalokasikan memori masih penting.
Itulah sebabnya Anda harus menghadiri presentasi saya dan menonton video kami di YouTube.
Fung Lam
Jadi, sebagai rangkuman, ada beberapa poin penting.
Pertama, kurangi.
Pastikan Anda mendapatkan ukuran gambar yang tepat.
Anda membagi gambar menjadi gambar yang lebih kecil agar tidak memuat semuanya ke dalam memori, ya, hanya untuk memuat gambar yang terlihat oleh pengguna ke dalam memori.
Gunakan bitmapConfig atau RGB_565 hardware jika memungkinkan.
Gunakan kembali, jadi pastikan Anda memiliki strategi cache, gunakan pemuat gambar untuk membantu Anda mengurangi jumlah Bitmap duplikat dalam memori.
Dan tentu saja, jangan gunakan beberapa gambar Bitmap, meskipun Anda mencoba menimpa ukuran cache default pemuat gambar tersebut.
Dan kita tidak menggunakan kembali, mendaur ulang.
Yap.
Manajemen memori aplikasi Anda harus beradaptasi dengan status aplikasi.
Jika UI disembunyikan, ya, Anda akan menggunakan lebih sedikit memori.
Jika OS benar-benar meminta Anda untuk melepaskan memori, lepaskan sebanyak mungkin.
Dan berhenti menguras resource, baik itu Bitmap, daya, atau resource komputasi.
Ya, hentikan timer banner, hentikan animasi, hentikan pengumpulan gambar.
Terakhir, pastikan Anda memantau.
Buka Konsol Google Play secara rutin.
Pastikan ini adalah latihan.
Ya, ini akan membantu Anda memahami, hei, apa status saat ini dari persentil ke-90 penggunaan memori aplikasi saya di seluruh populasi pengguna?
Hal ini juga akan membantu Anda mengidentifikasi masalah sejak dini.
Yap.
Untuk membuat profil secara lokal, Anda dapat menggunakan memory profiler Android Studio.
Kita juga memiliki beberapa perintah ADB.
Kami juga memiliki keterampilan Android.
Namun, pastikan jika Anda ingin melihat statistik untuk pengguna di dunia nyata, dan Konsol Google Play adalah tempat yang tepat untuk Anda.
Ada juga Google Play Reporting API yang dapat Anda gunakan untuk mendapatkan data tersebut menggunakan API tanpa perlu login ke Konsol Play.
Seperti biasa, jika Anda ingin mempelajari lebih lanjut, buka developer.android.com/memory.
Kurangi, gunakan kembali, dan daur ulang dengan ini.
Kita bisa membangun ekosistem Android yang lebih baik. Anda bisa, saya harap kita bersama-sama bisa membangun ekosistem Android yang lebih baik untuk mengatasi tantangan yang kita hadapi.
Terima kasih dan selamat melakukan coding.
Saya akan segera membuka waktu konsultasi di luar, jadi jika ada pertanyaan, silakan datang.
Dan pembicara berikutnya masih memiliki waktu, jadi harap tunggu pembicara berikutnya naik ke panggung dan terima kasih banyak.
// 1. Ask CDN for the exact UI card dimensions (e.g. 540x675)
val sizedUrl = "$cdnBaseUrl?w=$cardWidthPx&h=$cardHeightPx&fmt=webp"
// 2. Enforce exact downsampling + Hardware Bitmap config
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(sizedUrl)
.size(cardWidthPx, cardHeightPx) // Never use Size.ORIGINAL!
.precision(Precision.EXACT) // Downsample to exact card size
.bitmapConfig(Bitmap.Config.HARDWARE)
.build(),
contentDescription = product.title
)// Query active window bounds instead of hardcoding 1080 / 1920
val bounds = WindowMetricsCalculator.getOrCreate()
.computeCurrentWindowMetrics(activity).bounds
// Clamp target dimensions so Bitmap NEVER exceeds the active window
val safeWidth = minOf(requestedWidthPx, bounds.width())
val safeHeight = minOf(requestedHeightPx, bounds.height())
val bitmap = Bitmap.createBitmap(safeWidth, safeHeight, config)// Strip volatile query tokens (?from=list / ?sig=...) for cache key
val canonicalKey = MemoryCache.Key(productUrl.substringBefore("?"))
// On Checkout Screen: reuse the 200x200 Bitmap already loaded in the List!
AsyncImage(
model = ImageRequest.Builder(context)
.data(productUrl)
.placeholderMemoryCacheKey(canonicalKey) // Instant zero-decode reuse!
.memoryCacheKey(canonicalKey)
.diskCacheKey(productUrl.substringBefore("?"))
.build(),
contentDescription = product.title
)val totalBudgetBytes = computeAdaptiveCacheBytes(context)
val numberOfLoaders = 2 // e.g., Team A (Glide) + Team B (Coil)
// Approach A (With Stats): Weight by image traffic (70% Team A / 30% Team B)
val loaderABudget = (totalBudgetBytes * 0.70).toInt()
val loaderBBudget = (totalBudgetBytes * 0.30).toInt()
// Approach B (Simple Fix without stats): Divide by number of loaders!
val simpleSplitBudget = totalBudgetBytes / numberOfLoaders
MemoryCache.Builder()
.maxSizeBytes(simpleSplitBudget)
.build()override fun onStop() {
super.onStop()
// 1. Stop animations so they don't decode next frames in background!
bannerAnimationView.cancelAnimation()
animatedImageDrawable?.stop()
// 2. Cancel next-image buffering / prefetch queues!
nextImagesBufferingJob?.cancel()
Glide.with(this).pauseRequests()
}
override fun onDestroyView() {
super.onDestroyView()
bannerAnimationView.clearAnimation() // Release resources!
}adb shell dumpsys meminfo <pkg> # watch Native Heap!
adb shell am send-trim-memory <pkg> UI_HIDDEN