Android 在Observer内部使用AlarmManager会导致只调用一次observe()
我已经为一个报警应用程序实现了一个MVVM体系结构,该应用程序带有一个切换按钮,当报警打开时激活报警,当报警关闭时停用报警。单击ToggleButton调用ViewModel中的函数,该函数基于ToggleButton的切换状态,通过将值设置为带有报警信息的LiveData来激活或禁用报警 在片段中,我从ViewModel观察LiveData,并通过使用AlarmManager调度PendingEvent来设置警报,或者通过取消PendingEvent和AlarmManager来取消警报。问题是,当我第一次启动应用程序时,第一次单击ToggleButton会触发片段中的观察者,但此后,即使LiveData值发生更改,观察者也会拒绝触发 我发现,删除AlarmManager相关的函数(setExactAndAllowHileId()/cancel())会使观察者每次都被触发,但添加这些函数会使观察者不再对任何LiveData更改做出反应 我认为最好用代码和日志来解释 AlarmViewModel.ktAndroid 在Observer内部使用AlarmManager会导致只调用一次observe(),android,mvvm,alarmmanager,observer-pattern,android-livedata,Android,Mvvm,Alarmmanager,Observer Pattern,Android Livedata,我已经为一个报警应用程序实现了一个MVVM体系结构,该应用程序带有一个切换按钮,当报警打开时激活报警,当报警关闭时停用报警。单击ToggleButton调用ViewModel中的函数,该函数基于ToggleButton的切换状态,通过将值设置为带有报警信息的LiveData来激活或禁用报警 在片段中,我从ViewModel观察LiveData,并通过使用AlarmManager调度PendingEvent来设置警报,或者通过取消PendingEvent和AlarmManager来取消警报。问题是
private val newToast: MutableLiveData<SingleEvent<String>> = MutableLiveData()
private val activateEvent: MutableLiveData<SingleEvent<AlarmData>> = MutableLiveData()
private val deactivateEvent: MutableLiveData<SingleEvent<AlarmData>> = MutableLiveData()
...
private fun activateAlarm(alarmData: AlarmData) {
newToast.value = SingleEvent("Alarm has been set!")
activateEvent.value = SingleEvent(alarmData)
}
private fun deactivateAlarm(alarmData: AlarmData) {
newToast.value = SingleEvent("Alarm has been cleared")
deactivateEvent.value = SingleEvent(alarmData)
}
...
fun observeNewToast(): LiveData<SingleEvent<String>> = newToast
fun observeActivateEvent(): LiveData<SingleEvent<AlarmData>> = activateEvent
fun observeDeactivateEvent(): LiveData<SingleEvent<AlarmData>> = deactivateEvent
但是,如果我删除AlarmManager相关代码,观察者将一直被调用,正如预期的那样
修改的AlarmFragment.kt
@Inject
lateinit var alarmManager: AlarmManager
private val viewModel: SingleAlarmViewModel by lazy {
ViewModelProviders.of(activity!!).get(AlarmViewModel::class.java)
}
private lateinit var newToastObserver: Observer<SingleEvent<String>>
private lateinit var activateEventObserver: Observer<SingleEvent<AlarmData>>
private lateinit var deactivateEventObserver: Observer<SingleEvent<AlarmData>>
...
override fun onResume() {
super.onResume()
newToastObserver = Observer {
it?.getContentIfNotHandled()?.let { toastText ->
info("observeNewToast() -> message: $toastText")
Toast.makeText(activity, toastText, Toast.LENGTH_SHORT).show()
}
}
activateEventObserver = Observer {
it?.getContentIfNotHandled()?.let { alarmData ->
info("activateEventObserver -> $alarmData")
val idInteger = alarmData.timeInMillis.toInt()
val alarmTime = alarmData.timeInMillis
val activateAlarmIntent = Intent(activity, AlarmReceiver::class.java)
activateAlarmIntent.putExtra("message", alarmData.toString())
val pendingIntent = PendingIntent.getBroadcast(
activity,
idInteger,
activateAlarmIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
AlarmManagerCompat.setExactAndAllowWhileIdle(
alarmManager,
AlarmManager.RTC_WAKEUP,
alarmTime,
pendingIntent
)
}
}
deactivateEventObserver = Observer {
it?.getContentIfNotHandled()?.let { alarmData ->
info("deactivateEventObserver -> $alarmData")
val idInteger = alarmData.timeInMillis.toInt()
val deactivateAlarmIntent = Intent(activity, AlarmReceiver::class.java)
deactivateAlarmIntent.putExtra("message", alarmData.toString())
val pendingIntent = PendingIntent.getBroadcast(
activity,
idInteger,
deactivateAlarmIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
pendingIntent.cancel()
alarmManager.cancel(pendingIntent)
}
}
viewModel.observeNewToast().observe(activity!!, newToastObserver)
viewModel.observeActivateEvent().observe(activity!!, activateEventObserver)
viewModel.observeDeactivateEvent().observe(activity!!, deactivateEventObserver)
}
override fun onPause() {
super.onPause()
viewModel.apply {
observeNewToast().removeObserver(newToastObserver)
observeActivateEvent().removeObserver(activateEventObserver)
observeDeactivateEvent().removeObserver(deactivateEventObserver)
}
}
@Inject
lateinit var alarmManager: AlarmManager
private val viewModel: SingleAlarmViewModel by lazy {
ViewModelProviders.of(activity!!).get(AlarmViewModel::class.java)
}
private lateinit var newToastObserver: Observer<SingleEvent<String>>
private lateinit var activateEventObserver: Observer<SingleEvent<AlarmData>>
private lateinit var deactivateEventObserver: Observer<SingleEvent<AlarmData>>
...
override fun onResume() {
super.onResume()
newToastObserver = Observer {
it?.getContentIfNotHandled()?.let { toastText ->
info("observeNewToast() -> message: $toastText")
Toast.makeText(activity, toastText, Toast.LENGTH_SHORT).show()
}
}
activateEventObserver = Observer {
it?.getContentIfNotHandled()?.let { alarmData ->
info("activateEventObserver -> $alarmData")
val idInteger = alarmData.timeInMillis.toInt()
val alarmTime = alarmData.timeInMillis
val activateAlarmIntent = Intent(activity, AlarmReceiver::class.java)
activateAlarmIntent.putExtra("message", alarmData.toString())
val pendingIntent = PendingIntent.getBroadcast(
activity,
idInteger,
activateAlarmIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
// AlarmManager code commented out
// AlarmManagerCompat.setExactAndAllowWhileIdle(
// alarmManager,
// AlarmManager.RTC_WAKEUP,
// alarmTime,
// pendingIntent
// )
}
}
deactivateEventObserver = Observer {
it?.getContentIfNotHandled()?.let { alarmData ->
info("deactivateEventObserver -> $alarmData")
val idInteger = alarmData.timeInMillis.toInt()
val deactivateAlarmIntent = Intent(activity, AlarmReceiver::class.java)
deactivateAlarmIntent.putExtra("message", alarmData.toString())
val pendingIntent = PendingIntent.getBroadcast(
activity,
idInteger,
deactivateAlarmIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
pendingIntent.cancel()
// AlarmManager code commented out
// alarmManager.cancel(pendingIntent)
}
}
viewModel.observeNewToast().observe(activity!!, newToastObserver)
viewModel.observeActivateEvent().observe(activity!!, activateEventObserver)
viewModel.observeDeactivateEvent().observe(activity!!, deactivateEventObserver)
}
override fun onPause() {
super.onPause()
viewModel.apply {
observeNewToast().removeObserver(newToastObserver)
observeActivateEvent().removeObserver(activateEventObserver)
observeDeactivateEvent().removeObserver(deactivateEventObserver)
}
}
我已经尝试将ViewModel生命周期所有者设置为父活动,并将其设置回Fragment,但没有任何区别。删除AlarmManger相关的代码可以让观测者工作,但是由于我试图通过观测者设置警报,这让我非常沮丧。为什么AlarmManager让观察者在第一次之后停止被观察?它在什么地方被注销了吗
事先谢谢。原来这是我犯的完全不同的错误。。我错误地忘记了将AlarmManager注入到我应该通过Dagger注入的特定片段中。
谢谢你的贡献 因此,您取消了
停用EventTobServer
中的警报。但您希望警报在同一时间段后再次运行吗?或者在取消后是否再次调用activateEventObserver
。您可以调用activateEventObserver
再次设置警报,无论是在同一时间还是另一时间。在这个应用程序中,我打算一次只激活一个警报。
@Inject
lateinit var alarmManager: AlarmManager
private val viewModel: SingleAlarmViewModel by lazy {
ViewModelProviders.of(activity!!).get(AlarmViewModel::class.java)
}
private lateinit var newToastObserver: Observer<SingleEvent<String>>
private lateinit var activateEventObserver: Observer<SingleEvent<AlarmData>>
private lateinit var deactivateEventObserver: Observer<SingleEvent<AlarmData>>
...
override fun onResume() {
super.onResume()
newToastObserver = Observer {
it?.getContentIfNotHandled()?.let { toastText ->
info("observeNewToast() -> message: $toastText")
Toast.makeText(activity, toastText, Toast.LENGTH_SHORT).show()
}
}
activateEventObserver = Observer {
it?.getContentIfNotHandled()?.let { alarmData ->
info("activateEventObserver -> $alarmData")
val idInteger = alarmData.timeInMillis.toInt()
val alarmTime = alarmData.timeInMillis
val activateAlarmIntent = Intent(activity, AlarmReceiver::class.java)
activateAlarmIntent.putExtra("message", alarmData.toString())
val pendingIntent = PendingIntent.getBroadcast(
activity,
idInteger,
activateAlarmIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
// AlarmManager code commented out
// AlarmManagerCompat.setExactAndAllowWhileIdle(
// alarmManager,
// AlarmManager.RTC_WAKEUP,
// alarmTime,
// pendingIntent
// )
}
}
deactivateEventObserver = Observer {
it?.getContentIfNotHandled()?.let { alarmData ->
info("deactivateEventObserver -> $alarmData")
val idInteger = alarmData.timeInMillis.toInt()
val deactivateAlarmIntent = Intent(activity, AlarmReceiver::class.java)
deactivateAlarmIntent.putExtra("message", alarmData.toString())
val pendingIntent = PendingIntent.getBroadcast(
activity,
idInteger,
deactivateAlarmIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
pendingIntent.cancel()
// AlarmManager code commented out
// alarmManager.cancel(pendingIntent)
}
}
viewModel.observeNewToast().observe(activity!!, newToastObserver)
viewModel.observeActivateEvent().observe(activity!!, activateEventObserver)
viewModel.observeDeactivateEvent().observe(activity!!, deactivateEventObserver)
}
override fun onPause() {
super.onPause()
viewModel.apply {
observeNewToast().removeObserver(newToastObserver)
observeActivateEvent().removeObserver(activateEventObserver)
observeDeactivateEvent().removeObserver(deactivateEventObserver)
}
}
02-20 18:44:18.207 24565-24565/com.aly.alarm I/AlarmFragment: observeNewToast() -> message: Alarm has been cleared
02-20 18:44:18.222 24565-24565/com.aly.alarm I/AlarmFragment: deactivateEventObserver -> AlarmData(id=1, timeInMillis=1550730780000, isToggledOn=true)
02-20 18:44:18.367 24565-24565/com.aly.alarm I/AlarmFragment: observeNewToast() -> message: Alarm has been set!
02-20 18:44:18.382 24565-24565/com.aly.alarm I/AlarmFragment: activateEventObserver -> AlarmData(id=1, timeInMillis=1550730780000, isToggledOn=true)
02-20 18:44:21.277 24565-24565/com.aly.alarm I/AlarmFragment: observeNewToast() -> message: Alarm has been cleared
02-20 18:44:21.297 24565-24565/com.aly.alarm I/AlarmFragment: deactivateEventObserver -> AlarmData(id=1, timeInMillis=1550730780000, isToggledOn=true)
02-20 18:44:22.177 24565-24565/com.aly.alarm I/AlarmFragment: observeNewToast() -> message: Alarm has been set!
02-20 18:44:22.197 24565-24565/com.aly.alarm I/AlarmFragment: activateEventObserver -> AlarmData(id=1, timeInMillis=1550730780000, isToggledOn=true)
02-20 18:44:22.842 24565-24565/com.aly.alarm I/AlarmFragment: observeNewToast() -> message: Alarm has been cleared
02-20 18:44:22.857 24565-24565/com.aly.alarm I/AlarmFragment: deactivateEventObserver -> AlarmData(id=1, timeInMillis=1550730780000, isToggledOn=true)
02-20 18:44:23.447 24565-24565/com.aly.alarm I/AlarmFragment: observeNewToast() -> message: Alarm has been set!