20 Apr 2025Ā·7 menit membaca

Jaringan Kotlin untuk koneksi lambat: timeout dan retry aman

Jaringan Kotlin praktis untuk koneksi lambat: atur timeout, cache dengan aman, retry tanpa duplikat, dan lindungi aksi kritis di jaringan mobile yang fluktuatif.

Jaringan Kotlin untuk koneksi lambat: timeout dan retry aman

Apa yang rusak pada koneksi lambat dan fluktuatif

Di mobile, ā€œlambatā€ biasanya bukan berarti ā€œtidak ada internet.ā€ Seringkali artinya koneksi yang bekerja, tapi hanya dalam ledakan singkat. Satu permintaan bisa memakan 8–20 detik, macet setengah jalan, lalu selesai. Atau berhasil satu saat lalu gagal karena ponsel berpindah dari Wi‑Fi ke LTE, masuk area sinyal rendah, atau OS memasukkan app ke background.

ā€œFluktuatifā€ lebih buruk. Paket hilang, lookup DNS timeout, handshake TLS gagal, dan koneksi reset secara acak. Anda bisa menulis kode dengan benar dan tetap melihat kegagalan di lapangan karena jaringan berubah di bawah Anda.

Di sinilah pengaturan default cenderung gagal. Banyak aplikasi mengandalkan default library untuk timeout, retry, dan caching tanpa memutuskan apa yang ā€œcukup baikā€ untuk orang nyata. Default sering disetel untuk Wi‑Fi stabil dan API cepat, bukan untuk kereta komuter, lift, atau kedai kopi ramai.

Pengguna tidak menyebutkan ā€œsocket timeoutā€ atau ā€œHTTP 503.ā€ Mereka memperhatikan gejala: spinner yang tak berujung, error tiba‑tiba setelah menunggu lama (lalu berhasil saat dicoba lagi), tindakan ganda (dua pemesanan, dua pesanan, double charge), pembaruan yang hilang, dan status campuran di mana UI mengatakan ā€œgagalā€ tapi server sebenarnya berhasil.

Jaringan lambat mengubah celah desain kecil menjadi masalah uang dan kepercayaan. Jika aplikasi tidak memisahkan dengan jelas ā€œmasih mengirimā€ dari ā€œgagalā€ dan ā€œselesai,ā€ pengguna akan mengetuk lagi. Jika client retry secara membabi buta, itu bisa membuat duplikat. Jika server tidak mendukung idempotensi, satu koneksi goyah bisa menghasilkan beberapa penulisan yang ā€œberhasil.ā€

ā€œTindakan kritisā€ adalah apa saja yang harus terjadi paling banyak sekali dan harus benar: pembayaran, pengiriman checkout, pemesanan slot, transfer poin, ganti kata sandi, menyimpan alamat pengiriman, mengajukan klaim, atau mengirim persetujuan.

Contoh realistis: seseorang mengirim checkout di LTE lemah. Aplikasi mengirim request, lalu koneksi putus sebelum respons tiba. Pengguna melihat error, mengetuk ā€œBayarā€ lagi, dan sekarang dua request sampai ke server. Tanpa aturan jelas, aplikasi tidak tahu apakah harus retry, menunggu, atau berhenti. Pengguna juga tidak tahu apakah harus mencoba lagi.

Tentukan aturan sebelum menyetel kode

Saat koneksi lambat atau fluktuatif, sebagian besar bug muncul dari aturan yang tidak jelas, bukan dari HTTP client. Sebelum Anda menyentuh timeout, caching, atau retry, tuliskan apa arti ā€œbenarā€ untuk aplikasi Anda.

Mulailah dengan tindakan yang tidak boleh dijalankan dua kali. Ini biasanya berkaitan dengan uang dan akun: buat pesanan, charge kartu, kirim payout, ganti kata sandi, hapus akun. Jika pengguna mengetuk dua kali atau aplikasi retry, server harus tetap memperlakukan itu sebagai satu permintaan. Jika Anda belum bisa menjamin itu, tangani endpoint tersebut sebagai ā€œtidak auto‑retryā€ sampai Anda bisa.

Selanjutnya, tentukan apa yang boleh dilakukan setiap layar saat jaringan buruk. Beberapa layar masih berguna saat offline (profil terakhir, pesanan sebelumnya). Lainnya harus menjadi baca‑saja atau menampilkan status ā€œcoba lagiā€ yang jelas (jumlah inventori, harga langsung). Mencampur ekspektasi ini menghasilkan UI membingungkan dan caching yang berisiko.

Tetapkan waktu tunggu yang dapat diterima per tindakan berdasarkan cara pengguna berpikir, bukan apa yang terasa rapi di kode. Login bisa mentoleransi tunggu singkat. Upload file butuh lebih lama. Checkout harus terasa cepat tapi juga aman. Timeout 30 detik mungkin ā€œandalā€ di atas kertas dan tetap terasa rusak.

Terakhir, putuskan apa yang akan Anda simpan di perangkat dan berapa lama. Data cached membantu, tapi data usang bisa menyebabkan pilihan yang salah (harga lama, kelayakan yang kadaluarsa).

Tulis aturan itu di tempat yang dapat diakses semua orang (README cukup). Buat sederhana:

  • Endpoint mana yang ā€œtidak boleh duplikatā€ dan membutuhkan penanganan idempotensi?
  • Layar mana yang harus bekerja offline, dan mana yang baca‑saja saat offline?
  • Berapa waktu tunggu maksimum per tindakan (login, refresh feed, upload, checkout)?
  • Apa yang boleh di-cache di perangkat, dan kapan kedaluwarsa?
  • Setelah kegagalan, apakah Anda menampilkan error, mengantri untuk nanti, atau meminta retry manual?

Setelah aturan ini jelas, nilai timeout, header caching, kebijakan retry, dan status UI jauh lebih mudah diimplementasikan dan diuji.

Timeout yang sesuai ekspektasi pengguna nyata

Jaringan lambat gagal dalam berbagai cara. Setup timeout yang baik tidak hanya ā€œmemilih angka.ā€ Ia mencerminkan apa yang pengguna coba lakukan dan gagal dengan cepat cukup agar aplikasi bisa pulih.

Tiga timeout, dengan istilah sederhana:

  • Connect timeout: berapa lama menunggu untuk membangun koneksi ke server (lookup DNS, TCP, TLS). Jika ini gagal, request tidak benar‑benar dimulai.
  • Write timeout: berapa lama menunggu saat mengirim body request (upload, JSON besar, uplink lambat).
  • Read timeout: berapa lama menunggu server mengirim data kembali setelah request dikirim. Ini sering muncul di jaringan mobile yang tidak stabil.

Timeout harus mencerminkan layar dan taruhannya. Feed bisa lebih lambat tanpa bahaya besar. Tindakan kritis harus selesai atau gagal dengan jelas sehingga pengguna bisa memutuskan langkah selanjutnya.

Titik awal praktis (sesuaikan setelah pengukuran):

  • Memuat daftar (risiko rendah): connect 5–10s, read 20–30s, write 10–15s.
  • Pencarian sambil mengetik: connect 3–5s, read 5–10s, write 5–10s.
  • Tindakan kritis (risiko tinggi, seperti ā€œBayarā€ atau ā€œKirim pesananā€): connect 5–10s, read 30–60s, write 15–30s.

Konsistensi lebih penting daripada kesempurnaan. Jika pengguna mengetuk ā€œKirimā€ dan melihat spinner dua menit, mereka akan mengetuk lagi.

Hindari ā€œloading tak hinggaā€ dengan menambahkan batas atas yang jelas di UI juga. Tunjukkan progres segera, izinkan batal, dan setelah (misalnya) 20–30 detik tampilkan ā€œMasih mencobaā€¦ā€ dengan opsi untuk retry atau memeriksa koneksi. Itu menjaga pengalaman jujur meski library jaringan masih menunggu.

Saat timeout terjadi, log informasi yang cukup untuk men-debug pola nanti, tanpa mencatat rahasia. Field yang berguna termasuk path URL (bukan query lengkap), HTTP method, status (jika ada), rincian waktu (connect vs write vs read jika tersedia), tipe jaringan (Wi‑Fi, seluler, mode pesawat), perkiraan ukuran request/response, dan request ID agar Anda dapat mencocokkan log client dengan server.

Setup jaringan Kotlin sederhana dan konsisten

Saat koneksi lambat, inkonsistensi kecil di setup client berubah menjadi masalah besar. Baseline yang rapi membantu Anda debugging lebih cepat dan memberi setiap request aturan yang sama.

Satu client, satu kebijakan

Mulailah dengan satu tempat di mana Anda membangun HTTP client (sering satu OkHttpClient yang digunakan Retrofit). Letakkan dasar di sana sehingga setiap permintaan berperilaku sama: header default (versi app, locale, token auth) dan User‑Agent yang jelas, timeout diatur sekali (tidak disebar di berbagai panggilan), logging yang bisa diaktifkan untuk debugging, dan satu keputusan kebijakan retry (walau itu ā€œtidak ada retry otomatisā€).

Berikut contoh kecil yang menjaga konfigurasi di satu file:

val okHttp = OkHttpClient.Builder()
  .connectTimeout(10, TimeUnit.SECONDS)
  .readTimeout(20, TimeUnit.SECONDS)
  .writeTimeout(20, TimeUnit.SECONDS)
  .callTimeout(30, TimeUnit.SECONDS)
  .addInterceptor { chain ->
    val request = chain.request().newBuilder()
      .header("User-Agent", "MyApp/${BuildConfig.VERSION_NAME}")
      .header("Accept", "application/json")
      .build()
    chain.proceed(request)
  }
  .build()

val retrofit = Retrofit.Builder()
  .baseUrl(BASE_URL)
  .client(okHttp)
  .addConverterFactory(MoshiConverterFactory.create())
  .build()

Penanganan error pusat yang memetakan ke pesan pengguna

Error jaringan bukan sekadar ā€œsebuah exception.ā€ Jika setiap layar menangani secara berbeda, pengguna mendapat pesan acak.

Buat satu mapper yang mengubah kegagalan menjadi beberapa outcome ramah‑pengguna: tidak ada koneksi/mode pesawat, timeout, error server (5xx), error validasi atau auth (4xx), dan fallback tak dikenal.

Ini menjaga copy UI konsisten (ā€œTidak ada koneksiā€ vs ā€œCoba lagiā€) tanpa membocorkan detail teknis.

Tag dan batalkan request saat layar ditutup

Di jaringan fluktuatif, panggilan bisa selesai terlambat dan memperbarui layar yang sudah hilang. Jadikan pembatalan aturan standar: saat layar ditutup, pekerjaan juga berhenti.

Dengan Retrofit dan Kotlin coroutines, membatalkan coroutine scope (misalnya di ViewModel) membatalkan call HTTP yang mendasarinya. Untuk panggilan non‑coroutine, simpan referensi ke Call dan panggil cancel(). Anda juga bisa menandai request dan membatalkan grup panggilan saat fitur keluar.

Pekerjaan latar belakang tidak bergantung pada UI

Apa pun yang penting dan harus selesai (mengirim laporan, menyinkronkan antrian, menyelesaikan pengajuan) sebaiknya dijalankan di scheduler yang dirancang untuk itu. Di Android, WorkManager adalah pilihan umum karena bisa retry nanti dan bertahan saat app restart. Buat tindakan UI ringan, dan serahkan pekerjaan lebih lama ke background job bila masuk akal.

Aturan caching yang aman di mobile

Change specs without mess
Model PostgreSQL data and business logic in one place, then regenerate when requirements change.
Create project

Caching bisa sangat membantu pada koneksi lambat karena mengurangi unduhan ulang dan membuat layar terasa instan. Tapi juga bisa jadi masalah jika menampilkan data usang pada waktu yang salah, seperti saldo akun lama atau alamat pengiriman kadaluarsa.

Pendekatan aman adalah cache hanya yang pengguna tahan jika sedikit usang, dan paksa pengecekan segar untuk apa pun yang memengaruhi uang, keamanan, atau keputusan akhir.

Dasar Cache‑Control yang bisa Anda andalkan

Sebagian besar aturan turun ke beberapa header:

  • max-age=60: Anda dapat menggunakan kembali response cached selama 60 detik tanpa menanyakan ke server.
  • no-store: jangan simpan response ini sama sekali (terbaik untuk token dan layar sensitif).
  • must-revalidate: jika sudah kedaluwarsa, Anda harus memeriksa ke server sebelum menggunakannya lagi.

Di mobile, must-revalidate mencegah data ā€œsalah diam‑diamā€ setelah periode offline sementara. Jika pengguna membuka app setelah naik kereta bawah tanah, Anda menginginkan layar cepat, tapi juga ingin app mengonfirmasi apa yang masih benar.

Refresh ETag: cepat, murah, dan andal

Untuk endpoint baca, validasi berbasis ETag sering lebih baik daripada max-age panjang. Server mengirim ETag dengan response. Kali berikutnya, app mengirim If-None-Match dengan nilai itu. Jika tidak ada perubahan, server membalas 304 Not Modified, yang kecil dan cepat di jaringan lemah.

Ini bekerja baik untuk daftar produk, detail profil, dan layar pengaturan.

Aturan praktis singkat:

  • Cache endpoint ā€œreadā€ dengan max-age singkat plus must-revalidate, dan dukung ETag bila memungkinkan.
  • Jangan cache endpoint ā€œwriteā€ (POST/PUT/PATCH/DELETE). Perlakukan mereka selalu bergantung jaringan.
  • Gunakan no-store untuk apa pun yang sensitif (respons auth, langkah pembayaran, pesan privat).
  • Cache aset statis (ikon, config publik) lebih lama karena risiko usang rendah.

Jaga keputusan caching konsisten di seluruh app. Pengguna lebih mudah melihat ketidaksesuaian daripada penundaan kecil.

Retry aman tanpa memperburuk keadaan

Generate backend plus mobile
Create a production-ready Go backend and native mobile apps without hand-wiring every endpoint.
Start building

Retry terasa seperti perbaikan mudah, tapi bisa berbalik jika salah diterapkan. Retry request yang salah bisa menambah beban, menguras baterai, dan membuat app terasa tersangkut.

Mulailah dengan me‑retry hanya kegagalan yang cenderung sementara. Koneksi yang terputus, timeout baca, atau outage server singkat bisa berhasil pada percobaan berikutnya. Password salah, field hilang, atau 404 tidak akan.

Aturan praktis:

  • Retry timeout dan kegagalan koneksi.
  • Retry 502, 503, dan kadang 504.
  • Jangan retry 4xx (kecuali 408 atau 429, jika Anda punya aturan tunggu jelas).
  • Jangan retry request yang sudah mencapai server dan mungkin sedang diproses.
  • Batasi jumlah retry (sering 1 sampai 3 percobaan).

Backoff + jitter: kurangi gelombang retry

Jika banyak pengguna terkena outage yang sama, retry instan bisa menciptakan gelombang lalu lintas yang memperlambat pemulihan. Gunakan exponential backoff (menunggu lebih lama tiap kali) dan tambahkan jitter (delay acak kecil) supaya perangkat tidak retry serentak.

Contoh: tunggu ~0.5 detik, lalu 1 detik, lalu 2 detik, dengan random +/- 20% setiap kali.

Batasi total waktu retry

Tanpa batas, retry bisa menjebak pengguna dalam spinner selama menit. Pilih waktu total maksimum untuk seluruh operasi, termasuk semua jeda. Banyak app menargetkan 10–20 detik sebelum berhenti dan menampilkan opsi jelas untuk mencoba lagi.

Sesuaikan juga konteks. Jika seseorang mengirim formulir, mereka ingin jawaban cepat. Jika sinkronisasi background gagal, Anda bisa retry nanti.

Jangan pernah auto‑retry tindakan non‑idempotent (seperti membuat pesanan atau mengirim pembayaran) kecuali Anda memiliki perlindungan seperti kunci idempotensi atau pemeriksaan duplikat di server. Jika Anda belum bisa menjamin keamanan, gagallah dengan jelas dan biarkan pengguna memutuskan langkah selanjutnya.

Pencegahan duplikat untuk tindakan kritis

Di koneksi lambat atau fluktuatif, pengguna mengetuk dua kali. OS mungkin retry di background. Aplikasi Anda mungkin mengirim ulang setelah timeout. Jika tindakan itu ā€œmembuat sesuatuā€ (buat pesanan, kirim uang, ganti kata sandi), duplikat bisa merugikan.

Idempotensi berarti permintaan yang sama harus menghasilkan hasil yang sama. Jika permintaan diulangi, server tidak boleh membuat pesanan kedua. Ia harus mengembalikan hasil pertama atau mengatakan ā€œsudah dilakukan.ā€

Gunakan kunci idempotensi untuk setiap percobaan kritis

Untuk tindakan kritis, buat kunci idempotensi unik saat pengguna memulai percobaan, dan kirimkan dengan request (sering sebagai header seperti Idempotency-Key, atau sebagai field di body).

Alur praktis:

  • Buat UUID kunci idempotensi saat pengguna mengetuk ā€œBayarā€.
  • Simpan secara lokal dengan catatan kecil: status = pending, createdAt, hash payload request.
  • Kirim request dengan kunci tersebut.
  • Saat mendapat respons sukses, tandai status = done dan simpan ID hasil server.
  • Jika perlu retry, gunakan kembali kunci yang sama, bukan yang baru.

Aturan ā€œgunakan kembali kunci yang samaā€ inilah yang menghentikan double charge tidak sengaja.

Tangani restart aplikasi dan jeda offline

Jika app dimatikan saat request berlangsung, peluncuran berikutnya masih harus aman. Simpan kunci idempotensi dan status request di penyimpanan lokal (mis. baris kecil di database). Saat restart, baik retry dengan kunci yang sama atau panggil endpoint ā€œcek statusā€ menggunakan kunci yang tersimpan atau ID hasil server.

Di sisi server, kontraknya harus jelas: saat menerima kunci duplikat, server harus menolak percobaan kedua atau mengembalikan respons asli (ID pesanan yang sama, struk yang sama). Jika server belum bisa melakukan itu, pencegahan duplikat di sisi client tidak akan pernah sepenuhnya andal, karena app tidak bisa melihat apa yang terjadi setelah mengirim request.

Sentuhan yang bagus untuk pengguna: jika percobaan masih pending, tampilkan ā€œPembayaran diprosesā€ dan nonaktifkan tombol sampai Anda mendapat hasil final.

Pola UI yang mengurangi pengiriman ulang tidak sengaja

Turn rules into APIs
Design data models and API endpoints visually, then ship consistent behavior across clients.
Create API

Koneksi lambat tidak hanya merusak request. Mereka mengubah cara orang mengetuk. Saat layar membeku dua detik, banyak pengguna menganggap tidak terjadi apa‑apa dan menekan tombol lagi. UI Anda harus membuat ā€œsatu ketukanā€ terasa andal meski jaringan tidak.

Optimistic UI paling aman untuk aksi yang dapat dibatalkan atau berisiko rendah, seperti memberi bintang, menyimpan draft, atau menandai pesan sudah dibaca. Confirmed UI lebih baik untuk uang, inventori, hapus yang tak bisa dikembalikan, dan apa pun yang bisa membuat duplikat.

Default yang baik untuk tindakan kritis adalah status pending yang jelas. Setelah ketuk pertama, segera ubah tombol utama menjadi status ā€œSubmittingā€¦ā€, nonaktifkan, dan tampilkan baris singkat yang menjelaskan apa yang sedang terjadi.

Pola yang bekerja baik di jaringan fluktuatif:

  • Nonaktifkan aksi utama setelah ketukan dan pertahankan nonaktif sampai hasil final.
  • Tampilkan status ā€œPendingā€ yang terlihat dengan detail (jumlah, penerima, jumlah item).
  • Tambahkan tampilan ā€œAktivitas terbaruā€ agar pengguna bisa mengonfirmasi apa yang sudah mereka kirimkan.
  • Jika app di‑background, pertahankan status pending saat mereka kembali.
  • Utamakan satu tombol utama yang jelas daripada beberapa target ketuk pada layar yang sama.

Kadang request berhasil tapi respons hilang. Perlakukan ini sebagai hasil normal, bukan error yang mengundang ketukan ulang. Alih‑alih ā€œGagal, coba lagi,ā€ tampilkan ā€œKami belum yakinā€ dan tawarkan langkah aman seperti ā€œPeriksa status.ā€ Jika Anda tidak bisa memeriksa status, simpan catatan pending secara lokal dan beri tahu pengguna bahwa Anda akan memperbarui saat koneksi kembali.

Buat ā€œCoba lagiā€ eksplisit dan aman. Tampilkan hanya saat Anda bisa mengulangi request menggunakan ID request sisi client yang sama atau kunci idempotensi.

Contoh realistis: pengiriman checkout yang fluktuatif

Build for flaky networks
Build a mobile app and backend with clear retry and timeout rules from day one.
Try AppMaster

Seorang pelanggan berada di kereta dengan sinyal yang fluktuatif. Mereka menambahkan barang ke keranjang dan mengetuk Bayar. Aplikasi harus sabar, tapi juga tidak boleh membuat dua pesanan.

Urutan aman terlihat seperti ini:

  1. Aplikasi membuat ID percobaan sisi‑client dan mengirim request checkout dengan kunci idempotensi (mis. UUID disimpan bersama keranjang).
  2. Request menunggu connect timeout yang jelas, lalu read timeout yang lebih panjang. Kereta masuk terowongan, dan call timeout.
  3. Aplikasi retry sekali, tapi hanya setelah jeda pendek dan hanya jika tidak pernah menerima respons server.
  4. Server menerima request kedua dan melihat kunci idempotensi yang sama, sehingga mengembalikan hasil asli alih‑alih membuat pesanan baru.
  5. Aplikasi menampilkan layar konfirmasi final saat menerima respons sukses, bahkan jika itu datang dari retry.

Caching mengikuti aturan ketat. Daftar produk, opsi pengiriman, dan tabel pajak dapat di‑cache singkat (GET). Pengiriman checkout (POST) tidak pernah di‑cache. Bahkan jika Anda menggunakan cache HTTP, anggap itu sebagai bantuan baca untuk browsing, bukan sesuatu yang bisa ā€œmengingatā€ pembayaran.

Pencegahan duplikat adalah campuran pilihan jaringan dan UI. Saat pengguna mengetuk Bayar, tombol dinonaktifkan dan layar menampilkan ā€œMengirim pesanan...ā€ dengan opsi Cancel tunggal. Jika aplikasi kehilangan jaringan, ia beralih ke ā€œMasih mencobaā€ dan mempertahankan ID percobaan yang sama. Jika pengguna menutup paksa dan membuka lagi, app dapat melanjutkan dengan memeriksa status pesanan menggunakan ID itu, alih‑alih meminta mereka membayar lagi.

Daftar periksa cepat dan langkah selanjutnya

Jika aplikasi Anda terasa ā€œlumayan baikā€ di Wi‑Fi kantor tapi berantakan di kereta, lift, atau daerah pedesaan, anggap ini sebagai gerbang rilis. Pekerjaan ini lebih soal aturan jelas yang dapat diulang daripada kode cerdas.

Daftar periksa sebelum rilis:

  • Tetapkan timeout per tipe endpoint (login, feed, upload, checkout) dan uji pada jaringan dengan throttling dan latensi tinggi.
  • Retry hanya di tempat yang benar‑benar aman, dan batasi dengan backoff (beberapa kali coba untuk baca, biasanya tidak untuk tulis).
  • Tambahkan kunci idempotensi untuk setiap write kritis (pembayaran, pesanan, pengiriman formulir) sehingga retry atau double tap tidak membuat duplikat.
  • Buat aturan caching eksplisit: apa yang boleh disajikan usang, apa yang harus segar, dan apa yang tidak boleh di‑cache.
  • Buat status terlihat: pending, failed, dan completed harus berbeda, dan app harus mengingat tindakan yang selesai setelah restart.

Jika salah satu poin ini masih ā€œnanti kita putuskan,ā€ Anda akan berakhir dengan perilaku acak di berbagai layar.

Langkah selanjutnya agar ini bertahan

Tulis kebijakan jaringan satu halaman: kategori endpoint, target timeout, aturan retry, dan ekspektasi caching. Terapkan di satu tempat (interceptor, shared client factory, atau pembungkus kecil) sehingga setiap anggota tim mendapat perilaku yang sama secara default.

Kemudian lakukan drill duplikat singkat. Pilih satu tindakan kritis (mis. checkout), simulasikan spinner membeku, tutup paksa app, aktifkan mode pesawat, dan tekan tombol lagi. Jika Anda tidak bisa membuktikan itu aman, pengguna pada akhirnya akan menemukan cara untuk memecahkannya.

Jika Anda ingin menerapkan aturan yang sama antar backend dan client tanpa menulis semuanya secara manual, AppMaster (appmaster.io) bisa membantu dengan menghasilkan backend siap produksi dan kode native mobile. Meski begitu, kuncinya tetap kebijakan: definisikan idempotensi, retry, caching, dan status UI sekali, lalu terapkan secara konsisten di seluruh alur.

FAQ

What’s the first thing I should do before tweaking timeouts and retries?

Mulailah dengan mendefinisikan apa arti ā€œbenarā€ untuk setiap layar dan tindakan, terutama yang harus terjadi paling banyak sekali seperti pembayaran atau pemesanan. Setelah aturan jelas, atur timeout, retry, caching, dan status UI agar sesuai dengan aturan tersebut, jangan bergantung pada nilai default library.

What are the most common symptoms users notice on slow or flaky networks?

Pengguna biasanya melihat spinner tak berujung, error setelah menunggu lama, tindakan yang berhasil pada percobaan kedua, atau hasil duplikat seperti dua pesanan atau double charge. Ini sering disebabkan oleh aturan retry dan status ā€œpending vs failedā€ yang tidak jelas, bukan hanya sinyal yang buruk.

How should I think about connect, read, and write timeouts on mobile?

Gunakan connect timeout untuk seberapa lama menunggu membuka koneksi, write timeout untuk mengirim body request (upload), dan read timeout untuk menunggu respons setelah mengirim. Default yang masuk akal: timeout lebih pendek untuk bacaan berisiko rendah, dan read/write lebih panjang untuk pengiriman kritis, serta batasi UI supaya pengguna tidak menunggu selamanya.

If I can only set one timeout in OkHttp, which one should it be?

Ya — jika Anda hanya bisa mengatur satu, gunakan callTimeout untuk membatasi operasi keseluruhan sehingga Anda menghindari menunggu ā€œtak hinggaā€. Setelah itu, tambahkan connect/read/write timeout sesuai kebutuhan untuk kontrol yang lebih baik, terutama untuk upload dan respons lambat.

Which errors are usually safe to retry, and which aren’t?

Mulailah dengan me-retry hanya kegagalan sementara seperti putus koneksi, masalah DNS, dan timeout, serta kadang-kadang 502/503/504. Hindari retry untuk error 4xx dan hindari auto-retry pada operasi tulis kecuali Anda punya perlindungan idempotensi, karena retry bisa menghasilkan duplikat.

How do I add retries without making the app feel stuck?

Gunakan sedikit percobaan (sering 1–3) dengan exponential backoff dan sedikit jitter acak sehingga banyak perangkat tidak retry bersamaan. Juga batasi total waktu retry agar pengguna mendapat hasil jelas, bukan spinner yang berlangsung berjam-jam.

What is idempotency, and why does it matter for payments and orders?

Idempotensi berarti mengulangi permintaan yang sama tidak akan menghasilkan hasil kedua, jadi double tap atau retry tidak akan double-charge atau double-book. Untuk tindakan kritis, kirim kunci idempotensi per percobaan dan gunakan kembali pada retry agar server bisa mengembalikan hasil asli daripada membuat yang baru.

How should I generate and store an idempotency key on Android?

Buat kunci unik ketika pengguna memulai aksi, simpan secara lokal dengan catatan kecil ā€œpendingā€, dan kirimkan bersama request. Jika Anda retry atau aplikasi dimulai ulang, gunakan kembali kunci yang sama dan retry atau periksa status, sehingga niat pengguna tidak berubah menjadi dua penulisan di server.

What caching rules are safest for mobile apps on unreliable connections?

Cache hanya data yang relatif aman jika sedikit kadaluarsa, dan paksa pengecekan segar untuk hal-hal yang menyangkut uang, keamanan, dan keputusan akhir. Untuk read, utamakan kesegaran singkat plus revalidasi dan pertimbangkan ETag; untuk write, jangan cache, dan gunakan no-store untuk respons sensitif.

What UI patterns reduce double taps and accidental resubmits on slow networks?

Nonaktifkan tombol utama setelah ketukan pertama, tampilkan status ā€œSubmittingā€¦ā€ segera, dan pertahankan status pending yang terlihat yang tetap ada saat aplikasi di-background atau setelah restart. Jika respons bisa hilang, jangan memancing pengguna untuk mengetuk ulang; tunjukkan ketidakpastian (ā€œKami belum yakinā€) dan tawarkan langkah aman seperti memeriksa status.

Mudah untuk memulai
Ciptakan sesuatu yang menakjubkan

Eksperimen dengan AppMaster dengan paket gratis.
Saat Anda siap, Anda dapat memilih langganan yang tepat.

Memulai