Pembekuan dalam pengembangan — esensi, penyebab dan pencegahan

Penulis: IT Sectr Diterbitkan: 2026-07-28 Waktu membaca: 9 mnt

Pembekuan (visnet) — adalah kondisi di mana aplikasi seluler berhenti merespons tindakan pengguna untuk waktu yang lama. Berbeda dengan lag (perlambatan) dan glitch (perilaku salah), pembekuan sepenuhnya memblokir UI: sentuhan tidak diproses, animasi berhenti, layar „membeku". Penyebabnya — pemblokiran thread utama oleh operasi sinkron, deadlock dalam kode multi-thread, atau pengumpulan sampah yang sangat lama. Menurut Apple Main Thread Checker Documentation, lebih dari 40% laporan crash di iOS terkait dengan pemblokiran thread utama. Di Android, situasi serupa menyebabkan ANR — dialog sistem „Aplikasi tidak merespons".

Poin utama

  • Pembekuan — pemblokiran total UI untuk waktu yang lama (detik dan puluhan detik), berbeda dari lag dan glitch
  • Penyebab utama — pemblokiran thread utama oleh input-output, deadlock antar thread, loop tak terbatas dan kebocoran memori dengan GC panjang
  • Diagnostik mencakup Main Thread Checker di iOS, log ANR /data/anr/traces.txt di Android dan analisis dump thread
  • Perbaikan — memindahkan semua operasi yang berpotensi lama ke thread latar belakang, menggunakan Structured Concurrency dan menghindari synchronized di thread UI
  • Pencegahan — StrictMode, Main Thread Checker di skema Debug, analisis statis untuk deadlock dan uji coba berkala dengan pengukuran waktu respons

Apa itu pembekuan dalam pengembangan seluler

Pembekuan (freeze, hang) dalam aplikasi seluler — adalah kondisi di mana aplikasi berhenti memproses peristiwa input dan memperbarui antarmuka selama beberapa detik atau lebih. Secara teknis ini berarti thread utama (main thread) diblokir dan tidak dapat menjalankan siklus runner berikutnya.

Perbedaan antara pembekuan, lag, dan ANR

Lag — adalah keterlambatan hingga 500 ms, di mana pengguna melihat perlambatan, tetapi aplikasi terus berfungsi. Pembekuan berlangsung dari 1 detik hingga puluhan detik. ANR di Android — adalah kasus khusus pembekuan yang berlangsung lebih dari 5 detik dan terdeteksi oleh sistem. Tidak setiap pembekuan menyebabkan ANR, tetapi setiap ANR adalah pembekuan yang didokumentasikan oleh sistem.

Konsekuensi pembekuan

Di Android, pembekuan lebih dari 5 detik memicu dialog ANR dengan saran untuk menutup aplikasi. Di iOS, sistem memiliki watchdog — jika aplikasi tidak merespons peristiwa dalam 10–20 detik, Watchdog menghentikan proses dengan kode 0x8badf00d (ate bad food). Pengguna hanya melihat penutupan mendadak aplikasi dan kembali ke layar utama.

Penyebab pembekuan di Android dan iOS

Setiap operasi yang berlangsung lebih dari 100 ms dan dijalankan di thread utama berpotensi menyebabkan pembekuan. Mari kita lihat sumber utama pemblokiran.

Input-output sinkron di thread UI

Membaca file besar, permintaan jaringan tanpa asinkron, menyimpan data di SharedPreferences dengan metode sinkron apply diikuti commit — semua operasi ini memblokir thread utama. Di Android, pembacaan sinkron file 10 MB dapat memakan waktu 200–500 ms tergantung pada kecepatan memori flash. Di iOS, pemuatan sinkron URLSession tanpa completionHandler memblokir UI selama waktu respons server.

Deadlock dalam kode multi-thread

Ketika dua thread menunggu pelepasan sumber daya yang dipegang satu sama lain, terjadi deadlock. Dalam aplikasi seluler, skenario tipikal — thread A memblokir Lock1 dan menunggu Lock2, sementara thread B memblokir Lock2 dan menunggu Lock1. Kedua thread membeku selamanya. Jika salah satunya adalah thread utama, aplikasi membeku sepenuhnya.

Loop tak terbatas atau rekursi

Kesalahan logika — misalnya while(true) tanpa kondisi keluar atau rekursi tanpa kasus dasar — menyebabkan eksekusi tak terbatas di thread utama. Android mendeteksi ini melalui ANR setelah 5 detik, iOS — melalui Stackshot, yang merekam tumpukan panggilan yang berulang tak terbatas.

  • Android — Cursor tanpa ditutup, permintaan sinkron melalui execute() alih-alih enqueue(), FileInputStream.read() di thread UI
  • iOS — performSelectorOnMainThread:withObject:waitUntilDone:YES, menjalankan NSURLConnection sendSynchronousRequest, memuat gambar dengan dataWithContentsOfURL
  • Cross-platform — Flutter compute tanpa isolate khusus, React Native NativeModule sinkron

Cara mendiagnosis pembekuan

Diagnostik pembekuan memerlukan alat yang dapat merekam keadaan semua thread pada saat pemblokiran.

Log ANR di Android

Setiap ANR, sistem Android menyimpan file /data/anr/traces.txt, yang berisi dump tumpukan setiap thread aplikasi. Analisis file ini — metode diagnostik utama: temukan thread main dan lihat metode mana yang dihentikan. Jika tumpukan berakhir dengan Thread.sleep, InputStream.read, atau Lock.lock — penyebabnya ditemukan.

Stackshot di iOS

Xcode saat aplikasi membeku (sinyal SIGSTOP) dapat mengambil Stackshot — snapshot tumpukan semua thread. Aktifkan di skema „Logging" → „Include Stackshot Logs". Saat crash dengan kode 0x8badf00d, ekstrak log crash dari Devices & Simulators dan temukan thread com.apple.main-thread dengan tumpukan beku.

Main Thread Checker di Xcode

Main Thread Checker secara otomatis mendeteksi panggilan UIKit dari thread latar belakang selama aplikasi berjalan. Aktifkan di skema (Diagnostics → Main Thread Checker). Setiap peringatan adalah potensi penyebab pembekuan, terutama jika terjadi di penutupan completionHandler permintaan jaringan.

Contoh deteksi pemblokiran melalui StrictMode di Android:

kotlin
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
            .detectLeakedSqlLiteObjects()
            .detectLeakedClosableObjects()
            .penaltyLog()
            .build())
        StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
            .detectAll()
            .penaltyDeath()
            .build())
    }
}

Metode menghilangkan pemblokiran UI

Perbaikan pembekuan dimulai dengan memindahkan semua operasi yang berpotensi lama ke thread latar belakang. Mari kita lihat teknik spesifik untuk setiap platform.

Structured Concurrency dengan coroutine

Kotlin Coroutines dengan viewModelScope.launch(Dispatchers.IO) memastikan bahwa operasi jaringan atau pembacaan basis data dijalankan di thread latar belakang. Dispatchers.Main hanya digunakan untuk memperbarui UI. Penting: semua fungsi suspend harus terstruktur — coroutine anak dibatalkan saat induk dibatalkan, mencegah kebocoran thread.

Antrean asinkron di iOS

Grand Central Dispatch dengan DispatchQueue.global(qos: .userInitiated) untuk tugas latar belakang dan DispatchQueue.main.async untuk pembaruan UI — pola standar. Hindari sync() di antrean utama — ini deadlock yang terjamin. Gunakan async/await (Swift 5.5+) untuk kode asinkron yang lebih mudah dibaca dengan pengembalian otomatis ke thread utama melalui MainActor.

Menghindari synchronized di thread UI

Blok synchronized di Kotlin dan @synchronized di Swift di thread utama berbahaya: jika thread lain telah mengambil kunci ini, thread utama akan membeku menunggu. Gunakan tipe atomik (AtomicInteger, properti atomik di Swift) atau antrean sekuensial alih-alih kunci.

Contoh pemuatan data asinkron dengan coroutine di Android:

kotlin
class DataViewModel : ViewModel() {
    private val _data = MutableStateFlow<List<Item>>(emptyList())
    val data: StateFlow<List<Item>> = _data.asStateFlow()

    fun loadData() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = fetchFromNetwork()
            _data.emit(result)
        }
    }
}

Pencegahan pembekuan pada tahap pengembangan

Mencegah pembekuan secara sistematis membantu kombinasi alat, prinsip arsitektur, dan proses tinjauan kode.

StrictMode dengan penaltyDeath

Konfigurasikan StrictMode dengan penaltyDeath untuk kebijakan thread — ini akan menyebabkan aplikasi langsung crash saat mendeteksi panggilan jaringan atau I/O disk di thread utama. Pengembang tidak dapat mengabaikan masalah. Di build produksi, gunakan penaltyLog untuk mengumpulkan statistik tanpa crash.

Main Thread Checker di skema Debug

Di iOS, aktifkan Main Thread Checker di skema Debug dan konfigurasikan CI untuk menjalankan tes dengan opsi ini. Jika tes berisi panggilan UIKit dari thread latar belakang — harus gagal. Ini adalah satu-satunya cara yang dapat diandalkan untuk mendeteksi masalah sebelum dikirim ke TestFlight.

Tinjauan kode dengan pemeriksaan multi-thread

Tambahkan ke proses tinjauan kode poin wajib: periksa bahwa setiap panggilan jaringan, pekerjaan file, basis data, atau perhitungan berat dijalankan di thread latar belakang. Deadlock dapat dideteksi dengan penganalisis statis: Infer dari Facebook dan Thread Safety Checker dari Xcode menemukan potensi pemblokiran sebelum dijalankan.

  • Android — StrictMode, Infer, Android Lint Multithread, Kotlin Coroutines dengan viewModelScope
  • iOS — Main Thread Checker, TSAN (Thread Sanitizer), Xcode Analyze, Swift async/await dengan MainActor
  • Cross-platform — Flutter compute isolate, React Native interaction manager dengan requestAnimationFrame

Pertanyaan yang sering diajukan

Apa perbedaan antara pembekuan dan ANR?

ANR (Application Not Responding) — pemberitahuan sistem Android yang muncul saat thread utama membeku lebih dari 5 detik. Pembekuan adalah konsep yang lebih luas: setiap pemblokiran UI dengan durasi berapa pun. Di iOS tidak ada ANR, tetapi ada Watchdog dengan batas waktu 10–20 detik.

Bagaimana cara membaca traces.txt di Android?

File terletak di /data/anr/traces.txt. Untuk akses diperlukan root atau adb shell: jalankan adb shell cat /data/anr/traces.txt \> traces.txt dengan hak root. Dalam tumpukan, temukan thread „main" — metode yang terakhir dipanggil menunjukkan penyebab pemblokiran.

Mengapa aplikasi membeku di iOS tetapi tidak crash?

Jika pembekuan berlangsung kurang dari 10 detik, Watchdog tidak aktif, dan aplikasi hanya „membeku" hingga operasi pemblokiran selesai. Pengguna tidak melihat crash, tetapi mengalami frustrasi. Untuk mendeteksi kasus seperti itu, gunakan MetricKit dengan pelacakan waktu eksekusi kustom.

Bagaimana cara menguji aplikasi untuk pembekuan?

Gunakan tes UI dengan memeriksa bahwa layar terbuka dalam waktu kurang dari 1 detik. Tambahkan ke CI pengukuran waktu antara sentuhan dan munculnya layar berikutnya. Di Android, gunakan Espresso dengan IdlingResource untuk menunggu operasi asinkron. Di iOS, XCTest dengan XCTWaiter untuk memeriksa waktu muat.

Bisakah SwiftUI menyebabkan pembekuan?

SwiftUI sendiri tidak menyebabkan pembekuan, tetapi perhitungan kompleks di properti body — ya. Jika body dihitung selama 500 ms karena operasi berat, UI membeku. Solusi — pindahkan perhitungan ke Task.detached dan perbarui @State secara asinkron di aktor utama.

Ringkasan

  • Pembekuan — pemblokiran total UI selama detik dan puluhan detik, disebabkan oleh pemblokiran thread utama, deadlock, atau loop tak terbatas
  • Diagnostik — /data/anr/traces.txt di Android, Stackshot dan Main Thread Checker di iOS
  • Penyebab utama — I/O sinkron, deadlock antar thread, rekursi tak terbatas, GC panjang
  • Perbaikan — coroutine dengan dispatcher yang benar, async/await dengan MainActor, memindahkan semua operasi IO ke thread latar belakang
  • Pencegahan — StrictMode dengan penaltyDeath, Main Thread Checker, analisis statis deadlock (Infer, TSAN)
  • Di Android pembekuan > 5 dtk = ANR; di iOS > 10–20 dtk = Watchdog crash (0x8badf00d)
  • Rekomendasi: aktifkan Thread Sanitizer di skema Debug dan konfigurasikan CI untuk menjalankan tes dengan TSAN guna mendeteksi data race dan deadlock

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga