代码之家  ›  专栏  ›  技术社区  ›  leonardkraemer

取消时取消注册协同路由接收器

  •  0
  • leonardkraemer  · 技术社区  · 7 年前

    这个 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()
            }
    }
    

    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 ...
    
    1 回复  |  直到 7 年前
        1
  •  9
  •   leonardkraemer    7 年前

    答案确实是使用 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()
            }