Membuat aplikasi stopwatch adalah salah satu proyek awal yang bagus untuk developer Android yang baru belajar Kotlin. Ini adalah cara praktis untuk memahami bagaimana mengelola state aplikasi, memperbarui UI secara berkala, dan menangani interaksi pengguna. Meskipun terlihat sederhana, ada beberapa detail penting yang perlu diperhatikan agar stopwatch berjalan akurat dan efisien. Di tutorial ini, kita akan membangun stopwatch dasar di Android menggunakan Kotlin, lengkap dengan fungsi mulai, jeda, dan reset.
Sebagai seorang developer, saya sering menemukan kebutuhan akan fungsionalitas timer atau stopwatch, baik untuk fitur aplikasi maupun sebagai alat bantu debugging. Pendekatan yang akan kita gunakan di sini memanfaatkan Handler, sebuah mekanisme di Android yang cocok untuk menjadwalkan kode agar berjalan pada main thread (UI thread) setelah penundaan tertentu atau secara berulang. Ini krusial agar UI aplikasi tetap responsif dan tidak freeze.
Persiapan Proyek Android di Android Studio
Langkah pertama adalah menyiapkan proyek baru di Android Studio. Ikuti langkah-langkah berikut:
- Buka Android Studio.
- Pilih “New Project”.
- Pilih template “Empty Activity” dan klik “Next”.
- Konfigurasi proyek Anda:
- Nama Aplikasi: Misalnya, “MyStopwatchApp”
- Nama Paket: Biarkan default atau sesuaikan (misalnya,
com.tubianto.mystopwatchapp) - Bahasa: Pilih “Kotlin”
- Minimum SDK: Pilih versi yang Anda inginkan (misalnya, API 21 atau lebih tinggi)
- Klik “Finish” dan tunggu Android Studio menyelesaikan proses sinkronisasi Gradle.
Setelah proyek siap, kita akan memiliki sebuah MainActivity.kt dan file layout activity_main.xml yang siap diubah.
Desain Antarmuka Pengguna (activity_main.xml)
Untuk stopwatch kita, kita memerlukan sebuah TextView untuk menampilkan waktu dan tiga buah Button: Start, Pause, dan Reset. Kita akan menggunakan ConstraintLayout untuk menata elemen-elemen ini.
Buka file activity_main.xml di folder res/layout. Hapus TextView “Hello World” yang ada dan tambahkan elemen-elemen berikut. Penempatan dan ID elemen harus diperhatikan agar mudah diakses dari kode Kotlin.
Secara garis besar, layout akan berisi:
- Sebuah
TextViewbesar dengan IDtextViewTimeyang akan menampilkan waktu. Atur ukuran teks agar mudah dibaca, misalnyaandroid:textSize="64sp", dan atur rata tengah. - Tiga buah
Button:- Button “Start” dengan ID
buttonStart. - Button “Pause” dengan ID
buttonPause. - Button “Reset” dengan ID
buttonReset.
- Button “Start” dengan ID
Pastikan untuk memberikan batasan (constraints) yang tepat agar elemen-elemen ini terlihat rapi di berbagai ukuran layar. Misalnya, textViewTime di tengah atas, dan ketiga tombol di bagian bawah yang tersusun secara horizontal.
Implementasi Logika Stopwatch (MainActivity.kt)
Sekarang, mari kita beralih ke logika inti di MainActivity.kt. Kita akan memerlukan beberapa variabel untuk mengelola state stopwatch dan sebuah mekanisme untuk memperbarui waktu secara berkala.
Deklarasi Variabel Penting
Di dalam kelas MainActivity, kita akan mendeklarasikan variabel-variabel berikut:
private var timeInSeconds = 0L: Variabel ini akan menyimpan total waktu yang telah berlalu dalam milidetik (Lmenunjukkan tipeLong).private var isRunning = false: Sebuah boolean untuk melacak apakah stopwatch sedang berjalan atau tidak.private var handler = Handler(Looper.getMainLooper()): Ini adalah objekHandleryang akan kita gunakan untuk menjadwalkan tugas pada main thread. Kita perlu menginisialisasi denganLooper.getMainLooper()agar tugas dijalankan di UI thread.private lateinit var runnable: Runnable: SebuahRunnableyang akan berisi logika untuk memperbarui waktu dan UI. Kita akan menginisialisasinya nanti.
Fungsi untuk Memperbarui UI
Kita membutuhkan sebuah fungsi untuk mengonversi timeInSeconds menjadi format jam, menit, detik, dan milidetik yang mudah dibaca. Mari kita buat fungsi helper:
private fun formatTime(time: Long): String
Di dalam fungsi ini, kita akan melakukan perhitungan matematika sederhana:
val milliseconds = time % 1000val seconds = (time / 1000) % 60val minutes = (time / (1000 * 60)) % 60val hours = (time / (1000 * 60 * 60))
Kemudian, gunakan String.format untuk memformatnya menjadi “HH:MM:SS.mmm”. Misalnya, String.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds). Format %02d memastikan angka memiliki dua digit (misalnya 01, 05), dan %03d untuk milidetik agar tiga digit (001, 050).
Mengatur Runnable untuk Pembaruan Waktu
Di dalam metode onCreate, setelah memanggil setContentView, kita akan menginisialisasi runnable. Runnable ini akan melakukan dua hal:
- Meningkatkan
timeInSecondssebesar 10 (atau berapa pun interval yang Anda inginkan, misalnya 10 milidetik untuk simulasi milidetik). - Memperbarui teks
textViewTimedengan hasil dariformatTime(timeInSeconds). - Menjadwalkan dirinya sendiri untuk berjalan lagi setelah interval tertentu (misalnya, 10 milidetik) menggunakan
handler.postDelayed(runnable, 10).
Mengatur Listener untuk Tombol
Masih di onCreate, kita akan mengaitkan tombol-tombol yang sudah kita buat di XML dengan fungsi-fungsi di Kotlin. Kita menggunakan view binding untuk akses elemen UI yang lebih aman dan efisien.
Pastikan Anda mengaktifkan view binding di file build.gradle (Module: app):
Tambahkan baris berikut di dalam blok android:
buildFeatures { viewBinding true }
Setelah itu, di MainActivity.kt, Anda dapat menginisialisasi binding:
private lateinit var binding: ActivityMainBinding- Di
onCreate:binding = ActivityMainBinding.inflate(layoutInflater)dansetContentView(binding.root)
Kemudian, set OnClickListener untuk setiap tombol:
binding.buttonStart.setOnClickListener { startStopwatch() }binding.buttonPause.setOnClickListener { pauseStopwatch() }binding.buttonReset.setOnClickListener { resetStopwatch() }
Implementasi Fungsi Start, Pause, dan Reset
Buat tiga fungsi terpisah:
private fun startStopwatch()
Di fungsi ini:
- Periksa apakah
isRunningadalahfalse. Jika ya, lanjutkan. - Set
isRunning = true. - Panggil
handler.post(runnable)untuk memulai pembaruan waktu. - Atur status tombol: nonaktifkan tombol “Start”, aktifkan tombol “Pause” dan “Reset”.
private fun pauseStopwatch()
Di fungsi ini:
- Periksa apakah
isRunningadalahtrue. Jika ya, lanjutkan. - Set
isRunning = false. - Panggil
handler.removeCallbacks(runnable)untuk menghentikan pembaruan waktu. Ini sangat penting! - Atur status tombol: aktifkan tombol “Start”, nonaktifkan tombol “Pause”, aktifkan tombol “Reset”.
private fun resetStopwatch()
Di fungsi ini:
- Panggil
handler.removeCallbacks(runnable)untuk memastikan stopwatch berhenti jika sedang berjalan. - Set
isRunning = false. - Set
timeInSeconds = 0L. - Perbarui
textViewTimeke “00:00:00.000” menggunakanformatTime(0). - Atur status tombol: aktifkan tombol “Start”, nonaktifkan tombol “Pause”, nonaktifkan tombol “Reset”.
Mengelola Siklus Hidup Aplikasi
Penting untuk menghentikan handler saat aplikasi tidak lagi terlihat untuk menghemat daya baterai dan menghindari kebocoran memori. Kita bisa melakukannya di onPause atau onStop.
Override metode onPause():
override fun onPause() { super.onPause() if (isRunning) { handler.removeCallbacks(runnable) } }
Override metode onResume():
override fun onResume() { super.onResume() if (isRunning) { handler.post(runnable) } }
Untuk memastikan semua sumber daya dibersihkan, terutama jika Handler bisa menyebabkan kebocoran memori pada kasus-kasus tertentu, kita bisa menambahkan handler.removeCallbacks(runnable) di onDestroy() juga.
Pengalaman dan Pertimbangan Praktis
Dalam praktiknya, implementasi stopwatch dengan Handler seperti ini cukup efektif untuk banyak kasus. Namun, ada beberapa pertimbangan yang perlu Anda ketahui:
- Akurasi:
Handler.postDelayed()tidak menjamin akurasi waktu yang presisi milidetik, terutama jika main thread sibuk. Untuk aplikasi yang sangat membutuhkan presisi tinggi (misalnya untuk sains atau olahraga profesional), Anda mungkin perlu pendekatan yang lebih canggih seperti menggunakanSystem.nanoTime()untuk perhitungan delta waktu dan menjadwalkan ulang denganChoreographeratauValueAnimator. Namun, untuk aplikasi umum,Handlersudah lebih dari cukup. - Alternatif: Coroutines. Seiring berkembangnya Kotlin, Coroutines menjadi pilihan populer untuk menangani operasi asinkron. Anda bisa menggunakan
GlobalScope.launchatau scope yang terkait dengan siklus hidup komponen (misalnyalifecycleScopejika menggunakan ViewModel dan LiveData) dengan fungsidelay()untuk mencapai efek yang serupa. Ini menawarkan kode yang lebih bersih dan mudah dibaca. Namun, untuk pemula,Handlerseringkali lebih mudah dipahami konsep dasarnya. - State Management: Jika aplikasi Anda di-rotate atau dihentikan paksa oleh sistem, state stopwatch (
timeInSecondsdanisRunning) bisa hilang. Untuk aplikasi yang lebih kokoh, Anda harus menyimpan state ini menggunakanonSaveInstanceState()dan memulihkannya dionCreate()atauonRestoreInstanceState().
Masalah yang Sering Terjadi
Saat mengembangkan stopwatch, beberapa masalah umum yang sering dihadapi developer meliputi:
-
UI Tidak Update atau Lag
Gejala: Angka stopwatch tidak bergerak atau bergerak tersendat-sendat.
Penyebab: Pembaruan UI tidak dilakukan di main thread atau main thread terlalu sibuk.
Solusi: Pastikan Anda menggunakanHandler(Looper.getMainLooper())dan semua pembaruan UI dilakukan di dalamRunnableyang di-post ke main thread olehHandlertersebut. -
Stopwatch Jalan Terus Setelah Aplikasi Ditutup/Di-minimize
Gejala: Meskipun aplikasi sudah tidak terlihat, stopwatch masih berjalan di latar belakang, membuang baterai.
Penyebab:handler.postDelayed(runnable, delay)tidak dihentikan saat Activity di-pause atau di-destroy.
Solusi: Selalu panggilhandler.removeCallbacks(runnable)di metodeonPause()dan/atauonDestroy()untuk membersihkan semua tugas yang tertunda. -
Waktu Tidak Akurat (Terlalu Cepat/Lambat)
Gejala: Stopwatch tidak sinkron dengan waktu dunia nyata.
Penyebab: Interval pembaruan terlalu lama, atau ada penundaan di main thread yang mempengaruhi eksekusiRunnable.
Solusi: Untuk stopwatch dasar, ini biasanya tidak terlalu masalah. Jika Anda butuh akurasi tinggi, hindari mengandalkanpostDelayedsebagai satu-satunya sumber kebenaran. Anda bisa menyimpanSystem.currentTimeMillis()saat start, lalu menghitung selisihnya saat update. -
Status Tombol Salah (Misalnya, Bisa Klik “Start” Berkali-kali)
Gejala: Tombol “Start” bisa diklik berulang kali padahal stopwatch sudah berjalan, atau “Pause” bisa diklik saat stopwatch belum dimulai.
Penyebab: Logika untuk mengaktifkan/menonaktifkan tombol tidak tepat atau tidak memperhitungkan stateisRunning.
Solusi: Selalu perbarui statusisEnableddari tombol berdasarkan variabelisRunningdi setiap fungsistartStopwatch(),pauseStopwatch(), danresetStopwatch().
FAQ
Bagaimana jika saya ingin menyimpan waktu putaran (lap time)?
Untuk fitur lap time, Anda perlu menambahkan Button “Lap”. Saat tombol ini diklik, Anda bisa mengambil nilai timeInSeconds saat ini, menyimpannya ke dalam sebuah List (misalnya MutableList), dan menampilkannya di sebuah RecyclerView atau TextView lain di bawah stopwatch utama. Jangan lupa untuk mengelola tampilan lap time saat tombol “Reset” ditekan.
Bisakah saya menggunakan Kotlin Coroutines untuk stopwatch ini?
Tentu saja! Menggunakan Coroutines adalah pendekatan yang lebih modern di Kotlin. Anda bisa membuat Job yang di-launch pada lifecycleScope (untuk Activity/Fragment) dan menggunakan delay(10) di dalam loop untuk memperbarui waktu. Ini seringkali menghasilkan kode yang lebih ringkas dan mudah dibatalkan (cancellable).
Apakah saya perlu izin khusus di Android Manifest untuk stopwatch ini?
Tidak, untuk fungsionalitas stopwatch dasar seperti yang kita buat ini, Anda tidak memerlukan izin khusus di file AndroidManifest.xml. Semua operasi dilakukan secara lokal di perangkat.
Kesimpulan
Membangun aplikasi stopwatch di Android dengan Kotlin adalah proyek yang sangat baik untuk melatih pemahaman Anda tentang UI, manajemen state, dan pembaruan UI asinkron. Dengan memanfaatkan Handler, kita bisa dengan mudah membuat fungsionalitas timer yang responsif dan akurat untuk sebagian besar kebutuhan. Ingatlah selalu pentingnya membersihkan sumber daya seperti Handler saat aplikasi di-pause atau di-destroy untuk menghindari masalah kinerja dan kebocoran memori. Dari sini, Anda bisa mengembangkan fitur lebih lanjut seperti lap time, tema kustom, atau bahkan integrasi dengan notifikasi.
TAGS: Kotlin, Android, Stopwatch, Tutorial, Mobile Development, Android Studio, UI/UX, Programming, Developer Tools


