-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Handle methodchannel calls asynchronously to avoid blocking the UI #272
Conversation
ddd2bfd
to
5d5f0ec
Compare
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { | ||
final override fun onMethodCall(call: MethodCall, mcResult: MethodChannel.Result) { | ||
// Process all calls on a background thread, then post results back on the main thread | ||
asyncHandler.post { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the main fix - post work to an async handler, then post response back to mainHandler.
5d5f0ec
to
c75cd5a
Compare
@@ -26,7 +27,14 @@ abstract class BaseModel( | |||
val db: DB, | |||
) : EventChannel.StreamHandler, MethodChannel.MethodCallHandler { | |||
private val activeSubscribers = ConcurrentSkipListSet<String>() | |||
private val handler = Handler(Looper.getMainLooper()) | |||
protected val mainHandler = Handler(Looper.getMainLooper()) | |||
private val asyncHandlerThread = HandlerThread("BaseModel-AsyncHandler") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using HandlerThread
we can use a Coroutine
for a more lightweight thread.
Something like this:
private var parentJob = Job()
private val coroutineContext: CoroutineContext
get() = parentJob + Dispatchers.Main
private val scope = CoroutineScope(coroutineContext)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
including also that we avoid the need to keep initializing the Handler in order to keep working
private val asyncHandlerThread = HandlerThread("BaseModel-AsyncHandler") | ||
|
||
init { | ||
asyncHandlerThread.start() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Im not sure If we should have the HandlerThread always listening or in this case looping even when is not being called.
Thanks for the review @Crdzbird ! I'm going to stick with the Thread implementation since we use Threads already and the last time I tried introducing coroutines, I ended up with some surprising and difficult to understand bugs :) |
When finishing a voice memo, we've noticed that the loading indicator hangs while the voice memo gets encrypted. That's because we were handling the method channel calls on the main thread. This PR changes that so that they're handled on another thread.
While I was testing the VPN on/off button, I noticed that we were crashing with the below error.
The Android VPNService has a TUN device which we use to capture IP packets. It turns out that we're not supposed to close that device ourselves when the VPN stops, but we have been doing that. I think my phone must have a gotten a recent OS update that enforces that rule. So, while I was in here, I stopped closing that file descriptor.