You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Start documenting thread usage for service objects
This makes the current implicit assumption that the scan cycler is
started/stopped on the main thread explicit. This helps quickly inform
developers what the callback expectations are in regards to race
conditions and state safety.
In order to verify this we need to trace the `start`/`stop`/`destroy`
calls back to the service. The service's core methods `onCreate` and
`onDestroy` are called on the main thread (see links below). This means
the service creates it's `mMessenger` on the main thread, which creates
the `IncomingHandler` on the main thread which binds to the main looper.
However, the `IncomingHandler` class itself leaves the looper
unspecified relying on looper for the thread which creates it.
As we did in #430, this modifies `IncomingHandler` to explicitly use the
main looper as a future proofing mechanism. This way we can ensure this
implicit expectation doesn't change or at least make it obvious when
troubleshooting cases where someone expects it to have changed.
Similarly, this adds the main thread annotation to it's `handleMessage`
implementation.
Working from here we can confirm that the only places where the beacon
monitoring/ranging is updated is from the main thread through the
`IncomingHandler`. As the `IncomingHandler` is the main communication
channel for the service we transfer the main thread expectation to the
associated ranging/monitoring methods. _If_ someone decides to call
these methods directly on the service these annotations help the IDEs
check/document that such calls are expected from the main thread.
With all of that documented we can now confidently state that the scan
cycler's `start`, `stop`, and `destroy` are also meant to be called from
the main thread. As `start` and `stop` both call `scanLeDevice` we can
start tracing any other threading expectations for it. We already know
it's called from the main thread through deferred jobs. This leaves the
`finishScanCycle` as the last call site.
`finishScanCycle` is only called from `scheduleScanCycleStop`. As this
method name implies it's called through a deferred job on the main
thread. It is also called the "first" time in `scanLeDevice`. Thus we've
shown that `scanLeDevice`, `finishScanCycle`, and
`scheduleScanCycleStop` are expected to run on the main thread.
See Also:
- https://developer.android.com/reference/android/os/Handler.html#Handler%28%29
- https://developer.android.com/training/multiple-threads/communicate-ui.html
- https://developer.android.com/reference/android/app/Service.html
- https://developer.android.com/guide/components/services.html
- #430
0 commit comments