Android'de Thread Safety Hatası Yaptım ve Uygulamam Çöktü
Android geliştirirken thread safety ihmalı, en insidious hatalardan biri. Uygulamanız aylar sorunsuz çalışabilir, sonra belli koşullarda ani crash. Ben de yaşadım.
İlk hatam basitti: arka planda veri çekip doğrudan UI güncellemek. Bir `TextView`'ın metnini network thread'inde değiştiriyordum. Çoğu zaman çalışıyor, ama cihaz yavaşlayınca veya başka ağır işlem varsa `CalledFromWrongThreadException` fırlatıyordu. Çözüm: `runOnUiThread()` veya `Handler(Looper.getMainLooper())` kullanmak.
1// YANLIŞ2thread {3val data = fetchFromServer()4binding.textView.text = data // Crash!5}67// DOĞRU8thread {9val data = fetchFromServer()10runOnUiThread {11binding.textView.text = data12}13}Daha ince sorun `SharedPreferences`'te yaşadım. Birden fazla thread aynı anda okuma-yazma yapıyordu. `SharedPreferences` internal olarak senkronize ama dosya I/O sırasında veri bozulabiliyor. Çözdüm: `EncryptedSharedPreferences` ve tek bir `Executor`'da işlemleri sıraya koyduk.
Coroutine'lerle çalışanlar dikkat etsin. `GlobalScope` veya yanlış `Dispatcher` seçimi de thread safety problemine yol açar. Bir `ViewModel` içinde `viewModelScope` kullanmak, lifecycle ile senkron kalmanızı sağlar. `Dispatchers.Main` UI işleri için, `Dispatchers.IO` disk/network için zorunlu.
Gerçek sorun: bu hatalar deterministik değil. Cihaz hızlı, bellek bol olunca problem görünmüyor. Beta test, yoğun kullanım veya düşük spec cihazda ortaya çıkıyor. O yüzden kod review'da `thread`, `Handler`, `async` kelimelerini görünce dikkat kesiliyorum.
Android Lint'in `@UiThread` ve `@WorkerThread` annotationları var. Kullanın. Studio'nun static analysis'i çoğu hatayı yakalıyor, ama hepsini değil.
Sonuç: thread safety Android'de lüks değil, temel. Uygulamanız stabil görünse de, mimariye başlarken thread model düşün. Executor, Coroutine, LiveData, araç seçimi önemli, ama disiplin daha önemli.
0 yorum