Hilangnya koneksi — salah satu fenomena paling umum dan menjengkelkan di aplikasi seluler. Pengguna kehilangan akses ke data, operasi terputus, aplikasi macet atau crash. Menurut Google Android Developer Blog, 70% pengguna menghapus aplikasi jika aplikasi tersebut crash atau macet dua kali. Mari kita bahas penyebab hilangnya koneksi dan cara membangun komunikasi yang toleran terhadap kesalahan.
Poin Utama
Putus — istilah pengguna yang menggambarkan situasi ketika aplikasi kehilangan koneksi dengan server, berhenti merespons tindakan, atau berakhir dengan kesalahan. Dalam arti teknis, ini bisa berupa: kesalahan jaringan (timeout, DNS failure), ANR (pemblokiran thread UI), crash (pengecualian yang tidak ditangani) atau race condition (kondisi balapan).
Bagi pengguna, semua skenario ini terlihat sama: aplikasi berhenti bekerja. Perbedaan bagi pengembang terletak pada pendekatan diagnostik dan perbaikan. Kesalahan jaringan diselesaikan dengan mekanisme retry, ANR dengan memindahkan operasi keluar dari thread UI, crash dengan penanganan pengecualian.
Menurut Crittercism (sekarang Apteligent), rata-rata aplikasi seluler kehilangan 1-2% penggunanya pada setiap crash. Untuk aplikasi dengan 1 juta pengguna, ini berarti 10-20 ribu instalasi hilang per satu bug. Hal ini sangat kritis untuk aplikasi di sektor keuangan dan medis.
Jaringan tidak stabil — perangkat seluler terus-menerus beralih antara Wi-Fi dan jaringan seluler, memasuki zona tanpa jangkauan (metro, lift, ruang bawah tanah). Setiap peralihan menyebabkan hilangnya koneksi sementara yang harus ditangani oleh aplikasi dengan benar.
Time-out — jika server tidak merespons dalam waktu yang ditentukan (biasanya 10-30 detik), klien melempar SocketTimeoutException. Time-out yang lama tanpa umpan balik dianggap oleh pengguna sebagai macet. Disarankan untuk mengatur time-out tidak lebih dari 15 detik.
val client = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
Kondisi balapan (race condition) — terjadi ketika beberapa thread secara bersamaan membaca dan menulis data yang sama tanpa sinkronisasi. Misalnya, memuat data dari cache di thread UI secara paralel dengan pembaruan cache dari jaringan dapat menyebabkan menampilkan data yang usang atau tidak benar.
Offline-first — pola arsitektur di mana penyimpanan lokal (Room, CoreData) adalah satu-satunya sumber kebenaran. Jaringan digunakan untuk sinkronisasi data di latar belakang. Pengguna selalu melihat data terkini dari cache lokal, bahkan saat tidak ada jaringan.
Repository pattern — titik masuk tunggal untuk data yang memutuskan apakah akan mengambil data dari jaringan atau cache. Repositori mengabstraksi sumber data dari ViewModel dan UI. Pada kesalahan jaringan, repositori secara otomatis beralih ke sumber lokal.
class UserRepository(
private val api: UserApi,
private val dao: UserDao
) {
suspend fun getUsers(): Result<List<User>> {
return try {
val remote = api.fetchUsers()
dao.insertAll(remote)
Result.success(remote)
} catch (e: IOException) {
val cached = dao.getAll()
if (cached.isNotEmpty()) {
Result.success(cached) // kembalikan cache pada kesalahan jaringan
} else {
Result.failure(e)
}
}
}
}
Circuit Breaker — pola perlindungan server dari banjir permintaan saat tidak tersedia. Setelah N kesalahan berturut-turut, pemutus terbuka dan semua permintaan segera mengembalikan kesalahan tanpa mencoba koneksi. Setelah time-out tertentu, pemutus masuk ke status setengah terbuka untuk permintaan percobaan.
Exponential backoff — mekanisme retry standar. Setelah kegagalan pertama tunggu 1 detik, setelah kedua — 2 detik, lalu 4, 8, 16. Batasi jumlah percobaan maksimum (biasanya 3-5) agar tidak membebani server dan baterai.
Umpan balik pengguna — pada kesalahan jaringan tampilkan pesan yang dapat dipahami: "Tidak ada koneksi", "Server sementara tidak tersedia", "Periksa internet". Gunakan Snackbar atau Inline State View. Jangan pernah menampilkan kesalahan teknis (HTTP 500, SocketException) kepada pengguna.
ConnectivityManager — Android API untuk memantau jaringan. Izinkan aplikasi merespons perubahan: saat kehilangan jaringan tampilkan placeholder, saat pemulihan — perbarui data secara otomatis. Di iOS gunakan NWPathMonitor dari framework Network.
class NetworkMonitor(private val context: Context) {
private val manager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
fun isOnline(): Boolean {
val network = manager.activeNetwork ?: return false
val caps = manager.getNetworkCapabilities(network) ?: return false
return caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}
}
Crashlytics (Firebase) — alat pelaporan crash standar untuk aplikasi seluler. Mengumpulkan stacktrace dari semua pengecualian yang tidak ditangani, versi OS, model perangkat, dan waktu crash. Memungkinkan pengelompokan kesalahan dan penugasan penanggung jawab perbaikan.
Sentry — alternatif untuk Crashlytics dengan dukungan pemantauan kinerja. Memungkinkan pelacakan transaksi tertentu (misalnya, "otentikasi pengguna") dan melihat di langkah mana kesalahan terjadi. Performance tracing membantu membedakan time-out jaringan dari bug dalam logika aplikasi.
Timber — pustaka pencatatan untuk Android dengan penambahan tag otomatis berdasarkan kelas. Di build debug, catat semua permintaan dan respons jaringan. Di build release — hanya kesalahan dan peringatan melalui Crashlytics.setCustomLog.
| Alat | Tipe | Kapan digunakan |
|---|---|---|
| Crashlytics | Crash reporting | Selalu di release — pengumpulan crash otomatis |
| Sentry | Crash + Performance | Ketika perlu memprofilkan skenario pengguna tertentu |
| Timber | Logging | Debug: pencatatan lengkap; Release: hanya kesalahan |
| HTTP Toolkit | Network debug | Intersepsi dan analisis lalu lintas HTTP lokal |
Menurut Firebase Summit 2023, aplikasi yang menerapkan Crashlytics + Performance Monitoring mengurangi waktu rata-rata deteksi dan perbaikan bug kritis dari 3 hari menjadi 4 jam. Disarankan untuk mengatur peringatan pada setiap crash dengan frekuensi lebih dari 0,1% pengguna aktif.
Pertanyaan yang Sering Diajukan
Jika crash tidak tertangkap di Crashlytics, periksa native crash (SIGSEGV, SIGABRT) — mereka tidak ditangani oleh penangan pengecualian Java/Kotlin. Di Android ini bisa berupa kebocoran memori native dari JNI, di iOS — EXC_BAD_ACCESS. Gunakan Breakpad (Android) atau PLCrashReporter (iOS) untuk mengumpulkan stacktrace native crash.
Gunakan Network Link Conditioner (terbangun di iOS, untuk Android ada Facebook Network Connection Class atau pengaturan Developer Options > Network > Select network type). Atur penundaan 500-3000 ms dan kehilangan paket 5-30%. Juga dapat menggunakan Charles Proxy atau mitmproxy untuk emulasi penundaan jaringan dan pemutusan.
ANR terjadi ketika thread UI diblokir lebih dari 5 detik. Permintaan jaringan harus dijalankan di thread latar belakang: coroutines (viewModelScope.launch(Dispatchers.IO)), RxJava (subscribeOn(Schedulers.io)) atau WorkManager untuk sinkronisasi. Selalu atur time-out pada klien HTTP — tidak adanya time-out dapat menyebabkan pemblokiran abadi.
Race condition — situasi di mana hasil operasi tergantung pada urutan eksekusi thread. Misalnya, pengguna menekan tombol "Kirim" dengan cepat dua kali dan permintaan dikirim dua kali. Solusi: gunakan Mutex, single-threaded executors atau state machine (nonaktifkan tombol setelah tekan pertama). Di Kotlin gunakan Mutex dari coroutines atau anotasi @Synchronized.
Terapkan Chaos Engineering untuk aplikasi seluler: matikan jaringan selama operasi, simulasikan penundaan tinggi, beralih antara Wi-Fi dan jaringan seluler, bunuh proses oleh sistem. Alat: Facebook Network Connection Class, Charles Proxy, iOS Network Link Conditioner. Di CI/CD tambahkan tes UI dengan kondisi jaringan berbeda melalui AndroidTest Orchestrator.
Ringkasan
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.
Baca juga