tags:

views:

102

answers:

4

There are a lot of Android SDK APIs where callback handlers are registered. For a concrete example, with MediaPlayer you can set an onCompletionListener callback. Will these callbacks be called from the main (UI) thread? If the answer is "it depends", then I'm looking for some general rules for what callbacks will be called from the main thread versus another thread. The SDK documentation doesn't seem to spell it out. (Maybe I missed it.)

It seems important to know, because if I'm guaranteed main thread callbacks, then I can skip some thread synchronization on data shared between different places in code. If I'm forced to be pessimistic out of ignorance, then I have to write extra synch block code and worry about deadlocks, data integrity, and reduced performance.

+1  A: 

In my experience, those callbacks always come back on a non-UI thread. Have you tried Activity.runOnUiThread() to make sure your code runs on the UI thread? You would still take the performance hit because it takes longer for this code to run, but you would avoid some of the more common problems with thread synchronization.

Aaron C
+1  A: 

In general, the callbacks will occur on the thread where the event is running. If you register a callback and start something playing on a non-UI thread, then the callback will occur on the non-UI thread. However, Android won't go creating new threads in the background on its own.

All UI related events must occur on the UI thread, so you can gauarentee that click handler callbacks, etc will occur on the UI thread.

As Aaron C pointed out, you can use Activity.runOnUiThread to force things to occur there.

Additionally, the AsyncTask can be very useful for doing quick background work, where you need some completion step to be gauranteed to be on the uI thread.

edit: Example from the comments.

public void MyWorker {
   private OnCompleteListener onCompleteListener;

   public void setOnCompleteListener(OnCompleteListener onCompleteListener) {
     this.onCompleteListener = onCompleteListener;
   }

   public void doWork() {
     // do lots of work here
     onCompleteListener.onComplete();
   }
}

// somewhere in my Activity

public void onCreate() {
   final MyWorker worker = new MyWorker();
   worker.setOnCompleteListener(new OnCompleteListener() { ... });

   new Thread(new Runnable() {
       public void run() {
          worker.doWork();
       }
   }).start();
}

In this example, the onComplete "callback" will be run from the non-UI thread. The thread will exit after onComplete finishes.

Mayra
I don't understand how the case you describe for non-UI works. It seems that the non-UI thread will not have the same dispatching loop running in it that the UI thread would. I.e. there is some code in the UI thread that calls methods for UI events, view rendering, registered callbacks. But if I just create some thread with a Runnable-extending class, I think the JVM is just launching the thread and exiting, with no Android SDK hooks in it to put callbacks on the same thread. Maybe you were describing something else.
Erik Hermansen
A "callback" does not necessarily involve a dispatching loop. Say you have some class that does complex work. It has a setOnCompleteListener function, and a start function. On some none UI thread, you give it a OnCompleteListener, then call start. It goes off and does its thing, when its done it will call registeredOnCompleteListener.onComplete(). This will cause the onComplete function in your listener to be called from the thread the object was running on.
Mayra
Just to understand your example correctly: On thread 1, start() is called. On thread 2, inside the run() method, setOnCompleteListener() is called. Will the onComplete method execute in thread 1 or thread 2?
Erik Hermansen
No, there is no "run". Sorry, I think you are confusing my random example with a java Runnable. In my example, everything occurs on one thread, it just doesn't happen to be the main UI thread. The only point really was that there is no dispatching involved. You give the class an implementation of a listener. When it feels like it, it calls methods on your listener. These method invocations will occur on whatever thread happens to be running.
Mayra
I think unless there is a dispatching mechanism involved, the callback would necessarily need to be executed on a separate thread from the initial one. To make it concrete, suppose I do put the setOnCompleteListener() and start call inside of a Runnable-extending class. In this case, in which thread does the onCompleteListener() get called? It can't be the thread executing inside of the run() method, because that thread will close after run() exits.
Erik Hermansen
Yes it can, onComplete would be called from start, before start finishes, on the thread start is running on. Thus, run will not exit until after onComplete is finished. I'll edit post to make the example more clear.
Mayra
I think maybe I'm getting into something that should be split off into a separate more concrete question if we were to keep going. I believe I understand your point, but its a little different than what I'm asking. Thanks for the effort in explaining.
Erik Hermansen
+1  A: 

One case where Android will call your code on some other thread will be if you create a remote service, exposed via AIDL -- those AIDL methods will be called on a binder thread, not the main application thread.

However, that is the exception. As the others have noted, the vast majority of these are called on the main application thread.

CommonsWare
A: 

When in doubt you can use Log.i("TAG", Thread.currentThread().getName()); and see :)

dnkoutso