服务停止时,我的协程泄漏了广播接收器。这是因为服务在回调完成之前停止。如何取消协程以让我注销收款人的方式?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()
}
}
这是
BroadcastReciever
未在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:
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
,如下所示:
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()
}