7

I've read a few discussions about which thread calls various callback methods, for example those associated with Sensors. Most claim that the UI thread calls the callbacks - even when a separate worker thread is involved. Are we CERTAIN about that?

Consider this scenario: A separate class implements Runnable and SensorListener. The UI thread (during onCreate) starts the runnable and then goes back to its other business. The now-independent worker thread, in its own class, then registers the SensorListener.

Note that the UI thread never has any interaction with SensorManager nor SensorListener. The only thread that ever touches anything to do with Sensor, and the only class and member methods that it ever knows about, is the worker thread and its class.

It's hard for me to imagine that the UI thread would be calling the callback in this situation. Yet the online discussions are pretty "confident". Anyone know for certain?

Thanks!

AndroidNewbie
  • 515
  • 5
  • 17

1 Answers1

6

Are we CERTAIN about that?

Yes, though it depends on how you register the listener, and the behavior is not especially well-documented.

There are two registerListener() methods that take a SensorEventListener. One takes a Handler, the other does not. The latter one will use a Handler that is associated with the main application thread. If you wish to have the events delivered to a background thread, use a HandlerThread (which really should be called LooperThread, but they didn't ask me...), create a Handler in it, and use that Handler with registerListener().

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • The one I've been playing with is >>public boolean registerListener (SensorEventListener listener, Sensor sensor, int rate)<<. Its docs say nothing about a Handler. Are you saying that the callback method is put into the queue as a Runnable object? Otherwise I don't see how the UI thread would understand how to call the callback function if a Message appeared on its Handler. – AndroidNewbie Jul 16 '13 at 18:45
  • @AndroidNewbie: You are welcome to review the source code [for `SensorManager`](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/hardware/SensorManager.java) and [the concrete implementation `SystemSensorManager`](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/hardware/SystemSensorManager.java) at your leisure. Under the covers, Android currently creates one `Handler` per listener, using that `Handler` to dispatch events on the desired thread. – CommonsWare Jul 16 '13 at 18:55
  • Thanks for the reference, I'll dig into it. When you say "on the DESIRED thread" (emphasis mine), does it compel that to be the UI thread? A Handler can be supported by non-UI threads too. This situation concerns me because it feels like there could be thread safety and synchronization problems if, despite everything being set up by a worker thread, the UI thread is then the one that invokes the callback. If that callback method is a member of some separate class, then you'll basically have two threads working within a common instance. – AndroidNewbie Jul 16 '13 at 21:34
  • It's not obvious that thread sync would be necessary. Indeed, instancing a completely separate class that implements Runnable, and creating a dedicated thread for it, doesn't exactly shout that some other thread will have access to the private variables of the class but that's exactly what would happen! – AndroidNewbie Jul 16 '13 at 21:35
  • @AndroidNewbie: "does it compel that to be the UI thread?" -- please re-read my answer. If you use the three-parameter `registerSensorListener()`, `SystemSensorManager` will create a `Handler` on the `Looper` of the main application thread, where that `Looper` is passed into the `SystemSensorManager` constructor by `ContextImpl`. You can tell this **by reading the Android framework source code**. – CommonsWare Jul 16 '13 at 22:04
  • OK, I have read the source and agree with your info. Thank you very much for pointing me to this answer! However, it does beg one more question: If the interface between the SensorManager and a thread is based through a Handler, how is .onSensorChanged() called instead of the usual .handleMessage()? It's tempting to say that a Runnable is posted instead of a Message, but Runnable.run() does not match the calling parameters of onSensorChanged() because the latter takes a SensorEvent object. – AndroidNewbie Jul 18 '13 at 20:27
  • @AndroidNewbie: "how is .onSensorChanged() called instead of the usual .handleMessage()?" -- `handleMessage()` *is* called. It is called on the `SystemSensorManager`-defined `Handler`, which calls `onSensorChanged()`. – CommonsWare Jul 18 '13 at 20:39
  • Ohhhhhh, I see it now. It's not using the UI's default Handler, it's using a dedicated Handler for each registered Listener. (There are comments in the docs about all Handlers on a thread getting all messages, but I'll leave that alone for now.) So SensorManager's internal implementation of handleMessage() knows to call onSensorChanged(), with the proper parameters, because it is specific to SensorManager. I think I've got it now. Thank you very much for your patience and assistance! – AndroidNewbie Jul 18 '13 at 23:43