协程在注销时注销接收者 [英] Coroutine unregister reciever on cancel
本文介绍了协程在注销时注销接收者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
服务停止时,我的协程泄漏了广播接收器.这是因为服务在回调完成之前已停止.如何取消协程以让我取消注册接收者的方式?
My coroutine leaks a broadcast receiver, when the service is stopped. This is because the service stops, before the callback is finished. How can I cancel coroutine in a way that lets me unregister the reciever?
Service
的工作方式如下:
class DataCollectorService : Service(){
var job : Job? = null
override fun onStartCommand(...){
job = GlobalScope.launch {
val location = async { wifiScanner.getCurrentLocation() }
//other asynchronous jobs
location.await()
//do something with location
}
}
override fun onDestroy(){
job?.cancel()
}
}
这是在onDestroy
中未正确注销BroadcastReciever
的类:
Here is the class where the BroadcastReciever
is not unregistered properly in onDestroy
:
class WifiScanner(val context: ContextWrapper) {
val wifiManager: WifiManager
init {
wifiManager = context.baseContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
}
suspend fun getCurrentScanResult(): List<ScanResult> =
suspendCoroutine { cont ->
val wifiScanReceiver = object : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
if (intent.action?.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) == true) {
context.unregisterReceiver(this)
cont.resume(wifiManager.scanResults)
}
}
}
context.registerReceiver(wifiScanReceiver, IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
wifiManager.startScan()
}
}
stacktrace示例:
Example stacktrace:
de.leo.smartTrigger E/ActivityThread: Service de.leo.smartTrigger.datacollector.datacollection.DataCollectorService has leaked IntentReceiver de.leo.smartTrigger.datacollector.datacollection.sensors.WifiScanner$getCurrentScanResult$$inlined$suspendCoroutine$lambda$1@6febc2f that was originally registered here. Are you missing a call to unregisterReceiver()?
android.app.IntentReceiverLeaked: Service de.leo.smartTrigger.datacollector.datacollection.DataCollectorService has leaked IntentReceiver de.leo.smartTrigger.datacollector.datacollection.sensors.WifiScanner$getCurrentScanResult$$inlined$suspendCoroutine$lambda$1@6febc2f that was originally registered here. Are you missing a call to unregisterReceiver()?
at android.app.LoadedApk$ReceiverDispatcher.<init>(LoadedApk.java:1355)
at android.app.LoadedApk.getReceiverDispatcher(LoadedApk.java:1120)
at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1428)
at android.app.ContextImpl.registerReceiver(ContextImpl.java:1401)
at android.app.ContextImpl.registerReceiver(ContextImpl.java:1389)
at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:622)
at de.leo.smartTrigger.datacollector.datacollection.sensors.WifiScanner.getCurrentScanResult(WifiScanner.kt:35)
at de.leo.smartTrigger.datacollector.datacollection.DataCollectorService.getWifi(DataCollectorService.kt:219)
at de.leo.smartTrigger.datacollector.datacollection.DataCollectorService$uploadDataSet$1$wifi$1.invokeSuspend(DataCollectorService.kt:273)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
at kotlinx.coroutines.DispatchedTask$DefaultImpls.run(Dispatched.kt:221)
at kotlinx.coroutines.DispatchedContinuation.run(Dispatched.kt:67)
at ...
推荐答案
答案确实是使用suspendCancellableCoroutine
并定义cont.invokeOnCancellation
,如下所示:
The answer was indeed to use suspendCancellableCoroutine
and define cont.invokeOnCancellation
as written below:
suspend fun getCurrentScanResult(): List<ScanResult> =
suspendCancellableCoroutine { cont ->
//define broadcast reciever
val wifiScanReceiver = object : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
if (intent.action?.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) == true) {
context.unregisterReceiver(this)
cont.resume(wifiManager.scanResults)
}
}
}
//setup cancellation action on the continuation
cont.invokeOnCancellation {
context.unregisterReceiver(wifiScanReceiver)
}
//register broadcast reciever
context.registerReceiver(wifiScanReceiver, IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
//kick off scanning to eventually receive the broadcast
wifiManager.startScan()
}
这篇关于协程在注销时注销接收者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文