Seeking assistance with discrepancy in metrics bet...
# coroutines
s
Seeking assistance with discrepancy in metrics between coroutine inside and outside in a BroadcastReceiver
Hi everyone, I am experiencing an issue with event metrics in a
BroadcastReceiver
and a
ForegroundService
. There seems to be a discrepancy between the metrics collected inside and outside a coroutine, and I would appreciate your insights or advice. Code:
Copy code
class SampleForegroundService : Service() {

    init {
        startReceiver()
    }

    private fun startReceiver() {
        val filter = IntentFilter().apply {
            addAction(Intent.ACTION_SCREEN_ON)
        }

        SampleReceiver.getInstance().let { receiver ->
            ContextCompat.registerReceiver(
                this,
                receiver,
                filter,
                ContextCompat.RECEIVER_NOT_EXPORTED,
            )
        }
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
}

class SampleReceiver : BroadcastReceiver() {
    private val externalScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
    
    override fun onReceive(context: Context?, intent: Intent?) {
        if(intent?.action == Intent.ACTION_SCREEN_ON) {
            reportOutCoroutine()
            reportInCoroutine()
        }
    }

    private fun reportOutCoroutine() {
        // Reporting code
    }
    
    private fun reportInCoroutine() = externalScope.launch {
        runCatching {
            sampleUseCase()
        }.onSuccess { 
            // Reporting code
        }.onFailure { 
            // Failure reporting code
        }
    }

    companion object {
        private var instance: LockReceiver? = null
        fun getInstance(): LockReceiver {
            return instance ?: synchronized(this) {
                instance ?: LockReceiver().also {
                    instance = it
                }
            }
        }
    }
}
Issue: 1. There is a discrepancy between the metrics collected by
reportOutCoroutine()
and
reportInCoroutine()
. 2. The metric count from reportOutCoroutine is consistently higher. 3. Even under identical app versions and OS environments, there is about a 10% difference between the two metrics. Additional Information:
SampleForegroundService
is rarely destroyed. If it is, it gets recreated using
AlarmManager
. • I initially suspected a memory leak in
externalScope
, but this would primarily impact resource usage over time and not the execution of coroutines. • The discrepancy remains consistent across devices and configurations. Question: • Why might there be a difference in metrics between
reportOutCoroutine
and
reportInCoroutine
? • Have you experienced a similar issue, or do you have any suggestions on how to investigate or resolve this? Any advice or insights would be greatly appreciated. Thank you in advance for your help! 😊
s
Could you test/try this? Add a
delay(1000)
(or even a bit larger delay) as the first statement inside your
externalScope.launch { ... }
block. Does the discrepancy go even higher (more than 10%). If so, maybe the
externalScope
gets cancelled (not so likely) or the dispatcher may get starved...? Overall, BroadcastReceivers should do very very little work. If something needs to run in a background thread based on an event that is received by a BroadcastReceiver, an (Intent)Service should do the actual (background) work.
👀 1
thank you color 1
p
even if externalScope gets cancelled I’m not sure it could cause this given
reportInCoroutine()
wraps it with
runCatching
which breaks coroutine cancellation https://kotlinlang.slack.com/archives/C1CFAFJSK/p1679326509883299
s
@Peter Farlow I’ve confirmed that when the
CoroutineScope
is canceled, the
onFailure
block in
runCatching
does not log the event, which leads to a discrepancy in metrics. Thank you for the detailed explanation so far! I have an additional question: In the sample code below, the
CoroutineScope
is declared as a member variable of a
singleton instance
. It uses
SupervisorJob
to prevent cancellation from child coroutines, and no explicit cancellation is being performed. Are there any scenarios in which this scope could still be canceled? Additionally, I’d appreciate any advice on how to ensure the scope remains stable or solutions to prevent such issues. 😊
s
I think, but that would need to be proven, that you should avoid long(ish) running, delayed, background tasks/threads in a BroadcatlatReceiver. If you need that, do that inside an (Intent)Service instead. BroadcastReceivers should really only be used to trigger other tasks (eg kick of an IntentService).
s
@streetsofboston Thanks for the advice, I'll take note 😄