Android bildirim eylemleri doğru şekilde: uygulamayı açmadan bir dozu alındı olarak işaretlemek
Android bildirim eylem düğmeleri için pratik bir rehber — PendingIntent'ler, trambolin kısıtlaması, goAsync() ve Room durumunu güvenle güncellemek.
Bir hatırlatma, onu kapatmak için uygulamayı açmanızı gerektiriyorsa, kimsenin istemediği bir adım eklemişsinizdir demektir. Bir bildirimin tüm amacı, arayüzün kendisi olmasıdır — kullanıcı ona tepki verir ve devam eder. Bir ilaç hatırlatıcısı olan OldSchool tam olarak buna bağlı yaşıyor: Alındı, Ertele ve Atla için kilit ekranı düğmeleri anında çalışmalı, doğru veriyi güncellemeli ve kullanıcının hiç istemediği bir Activity’yi asla başlatmamalı. İşte bunun nasıl inşa edildiği ve Android’in bunu gereğinden zor kıldığı iki üç nokta.
”Sadece uygulamayı aç” sorunu
Bir hatırlatma bildiriminin saf hâli, bir düğmeye basılan bir ekrana uygulamayı açan tek bir dokunuştur. Bu, tek olması gereken bir şey için iki fazla eylemdir. Ayrıca her seferinde bir Activity’yi soğuk başlatmak anlamına gelir — hafızada bir düzine başka uygulama olan orta seviye bir telefonda bu, “hatırlatmayı gördüm” ile “alındı olarak işaretlendi” arasında görünür bir gecikmedir.
Çözüm NotificationCompat.Action: bildirime doğrudan eklenen ve bir Activity’yi hiçbir zaman ön plana getirmeden ateşlenen düğmeler.
val takenIntent = Intent(context, DoseActionReceiver::class.java).apply {
action = ACTION_MARK_TAKEN
putExtra(EXTRA_DOSE_ID, doseId)
}
val takenPendingIntent = PendingIntent.getBroadcast(
context,
doseId.hashCode(),
takenIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("Metformin — 500mg")
.addAction(R.drawable.ic_check, "Taken", takenPendingIntent)
.addAction(R.drawable.ic_snooze, "Snooze", snoozePendingIntent)
.addAction(R.drawable.ic_skip, "Skip", skipPendingIntent)
.build()
Üç düğme, üç PendingIntent, her biri bir Activity yerine bir BroadcastReceiver’a işaret ediyor. Bu ayrım, işin tamamı.
Neden bir Activity değil de bir receiver
Eylemi bir Activity üzerinden yönlendirmek cazip gelebilir — zaten bir tane var, zaten veritabanınızla konuşuyor. Yapmayın. Android 12’den bu yana, bir bildirim eyleminden doğrudan bir Activity başlatmak (bildirimin ana dokunma hedefinden değil) bir “activity trambolini” olarak kısıtlanıyor ve hâlâ çalıştığı yerlerde bile istenmeyen bir arayüz yanıp sönmesi, görev değiştiricide bir giriş ve daha yavaş bir yanıt anlamına geliyor. Bir BroadcastReceiver bunların hiçbirini yapmaz — çalışır, işini yapar ve bildirimin güncellenmesi dışında görünür hiçbir şey olmaz.
FLAG_IMMUTABLE da önemli: Android 12’den itibaren, bir RemoteInput gibi açıkça FLAG_MUTABLE gerektirmeyen her şey için değiştirilebilir PendingIntent’ler varsayılan olarak engelleniyor. Sıradan bir eylem düğmesinin değiştirilebilirliğe ihtiyacı yok — onu immutable işaretleyin ve devam edin.
Bir receiver’da gerçek iş yapmak: goAsync()
BroadcastReceiver.onReceive() ile ilgili sıkıntı, neredeyse anında dönmesinin beklenmesidir — birkaç milisaniye, Room veritabanına arka planda yazmanın aldığı süre değil. Yazma işlemi bitmeden dönerseniz, sistem bazı OEM arayüzlerinde süreci yazma sırasında öldürebilir.
goAsync() bu ek süreyi doğru şekilde satın alır:
class DoseActionReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val doseId = intent.getLongExtra(EXTRA_DOSE_ID, -1)
val pendingResult = goAsync()
CoroutineScope(Dispatchers.IO).launch {
try {
doseDao.markTaken(doseId, timestamp = System.currentTimeMillis())
NotificationManagerCompat.from(context).cancel(doseId.toInt())
} finally {
pendingResult.finish()
}
}
}
}
goAsync() size yaklaşık 10 saniyelik uzatılmış bir ömür verir — bir Room yazması için yeterli, bir foreground service’i haklı çıkaracak kadar değil. pendingResult.finish() fonksiyonunu her zaman bir finally bloğunda çağırın; bir hata yolunda bunu unutmak, sistemin serbest bırakamadığı bir wakelock tutmasına neden olur ve sonunda receiver’ınızı yanlış davranan olarak işaretler.
Bildirimi ve veritabanını dürüst tutmak
Bir dozu “alındı” olarak işaretlemek yalnızca bir veritabanı satırı değildir — bildirimin kendisi de kaybolmalı ya da değişmeli, yoksa kullanıcı ona tekrar dokunur ve neden hiçbir şey olmadığını merak eder. Yazma işleminden sonra NotificationManagerCompat.cancel() çağırmak asgari düzeydir; Ertele için, orijinal hatırlatmanın kullandığı aynı AlarmManager/WorkManager yolu üzerinden yeniden zamanlıyorum (zamanlama yarısı için WorkManager ve tam alarmlar yazısına bakın) ikinci bir bildirim hattı icat etmek yerine. Bir hatırlatma, bir kod yolu — ilk seferinde ateşlense de üçüncüsünde ateşlense de fark etmez.
Diğer kolay hata, hiçbir şeyin gözlemlemediği bellek içi durumu güncellemektir. Gün görünümü Room’dan bir Flow tarafından besleniyorsa, receiver’ın yazması yeterlidir — arayüz bir sonraki görünür olduğunda yeniden oluşur, sıfır açık senkronizasyon kodu ile. Değilse, kullanıcı uygulamaya döndüğü anda eski veriyle karşılaşırsınız ve sonunda gerçekte eksik bir observer olan bir yarış durumunu ayıklamayla uğraşırsınız.
Bunun size kazandırdığı
Kağıt üzerinde ölçülebilir fark dramatik değil — bir ekran daha az, bir dokunuş daha az — ama bir hatırlatmanın hissini değiştiriyor. Ortaya çıktığı yerde bir saniyeden kısa sürede kendini çözen bir bildirim, ona verdiğiniz iki saniyeye saygılı okunur. Soğuk bir uygulama açılışı talep eden bir bildirim ise sürtünme olarak okunur ve sürtünme, bir hatırlatmayı kullandığınız bir şey yerine alışkanlıkla kaydırıp geçtiğiniz bir şeye dönüştüren şeydir.
Herhangi bir “buna şimdi tepki ver” bildirimi inşa ediyorsanız — bir hatırlatma, bir yapılacaklar dürtmesi, bir teslimat onayı — desen alan ne olursa olsun geçerli: eylemleri bir BroadcastReceiver üzerinden yönlendirin, pencerenizi goAsync() ile uzatın, mevcut veri katmanınız üzerinden yazın ve değişikliği manuel olarak itmek yerine reaktif bir arayüzün onu almasına izin verin. Kullanıcıların yalnızca eksik olduğunda fark ettiği bir özellik için küçük bir tesisat işi.
// İlgili okumalar
Günlükten dahası
2026'da güvenilir Android hatırlatıcıları: WorkManager, kesin alarmlar ve yeni pil kuralları
2026'da Android'de gerçekten tetiklenen hatırlatıcılar nasıl yayınlanır — WorkManager mı AlarmManager mı, SCHEDULE_EXACT_ALARM, POST_NOTIFICATIONS ve hâlâ can yakan OEM tuhaflıkları.
Gerçekten uyacağın bir ilaç rutini kurmak
İlaç uyumuna dair pratik bir rehber — insanlar dozları neden kaçırır, hatırlatıcı zamanlaması gerçekte nasıl işler ve kötü bir haftadan da sağ çıkan bir rutin nasıl kurulur.
İrade gücüne dayanmayan bir hidrasyon rutini
Çoğu hidrasyon tavsiyesi 'daha fazla su iç'e indirgenir ve aradaki boşluğu irade gücünün dolduracağını umar. İşte bunun yerine ipuçları ve nazik dürtmeler üzerine kurulu bir rutin.