Callback from another thread causes an exception - android

I'm trying to create a simple multiplayer game. There's a WorkerService which is supposed to handle all network communication and all interaction between this service and my Activities is done with AIDL. I think this is a standard approach - to enable two way interaction I use also an IWorkerCallback interface (also AIDL).
The problem is that callbacks have to change things in UI which may be done only in UI thread. I've created a Handler (in UI thread) and believed that this is an obvious solution. But, surprisingly, it's not working.
My LoungeActivity calls startServer() method of IWorker interface. Corresponding method of my WorkerService does some job and makes a callback - this works fine. Then WorkerService spawns a new thread and callback from this thread results in a bad Exception being thrown:
Can't create handler inside thread that has not called Looper.prepare()
Here's some code to make it clear:
startServer() implementation:
private void startServerImpl(String name, float latStart, float latEnd,
float lonStart, float lonEnd)
{
// some instructions here
// this works fine:
callback.notifySocketCreated();
// my naughty thread:
new ServerThread().start();
// some instructions here
}
ServerThread code:
private class ServerThread extends Thread {
#Override
public void run()
{
//some instructions here
// this call will cause an error
callback.notifyGameRegistered();
}
}
Every method from callback looks like that:
public void notifyGameRegistered() throws RemoteException
{
handler.dispatchMessage(handler.obtainMessage(CALLBACK_GAME_REGISTERED));
}
In Handler's handleMessage() method I'm doing a simple switch(msg.what) and in every case there's a simple UI modification (showing a Toast, changing Text, etc.).
I have no idea why is this Exception thrown.. I've managed to fix it by packing code into a Runnable and calling runOnUiThread() but it still makes me curious - shouldn't a Handler always run in thread that created it? Or maybe I'm doing something wrong?

I know this is a bit late - but the problem is that you called dispatchMessage().
The correct method is sendMessage().
dispatchMessage() will call handleMessage() on the same thread.
I am guessing the problem is not your Handler being on the wrong thread - but that the UI is trying to create a Handler somewhere in yout onHandle() method. Because onHandle() is called on the wrong thread, you are getting an exception.
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg)
{
if (msg.callback != null) {
handleCallback(msg);
} else {
handleMessage(msg);
}
}

You have to somehow call the offending function from the main thread.

The function that changes the UI should be in the activity that owns the UI.
This link should help you out:
http://android-developers.blogspot.com/2009/05/painless-threading.html

Related

How to implement a more flexible AsyncTask?

while it is very convenient to use, from my understanding, AsyncTask has two important limitations:
doInBackground of any instances will share the same worker
thread, i.e. one long running AsyncTasks can block all others.
execute, onPostExecute and other "synchronizing" methods must/will always be executed on the UI-thread, i.e. not on the Thread, which wants to start the task.
I ran into trouble, when I tried to reuse some existing AsyncTasks in a background IntentService that are responsible for the client-server communication of my app. The tasks of the service would fight over time in the worker thread with those of the UI activities. Also they would force the service to fall back onto the UI-thread, although that service should perform its work quietly in the background.
How would I go about removing/circumventing these limitations? I basically want to achieve:
A framework that closely resembles AsyncTask (because I need to migrate a lot of critical code there).
Each instance of such a task should run its doInBackground on its own thread instead of a single worker thread for all instances.
Edit: Thx to VinceFR for pointing out this can be achieved by simply calling executeOnExecutor instead of execute.
The callbacks like onPostExecute should be called on the same thread that started the task by calling execute, which should not need to be the UI-thread.
I figure, I'm not the first person to require something like this. Therefore I wonder: Is there already some third-party library that can be recommended to accomplish this? If not, what would be a way to implement this?
Thanks in advance!
The solution looks like this:
All classes that spawn AsyncTasks that might interfere with each other get their own Executor like this one (make that elaborate as you like using thread pools etc.):
private Executor serviceExecutor = new Executor() {
public void execute(Runnable command) {
new Thread(command).start();
}
};
As pointed out by VinceFR you can run an AsyncTask on a given Executor by calling it like this (where payload are the parameters that you would regularly pass to a task):
task.executeOnExecutor(serviceExecutor, payload);
However, this breaks backwards-compatibility to Gingerbread and earlier. Also, if you want to support Honeycomb, you need to make sure, this call happens on the UI thread. Jelly Bean will take care of this automatically.
Now the trickier part: Keeping the service running on its own thread. As many things in Android this seems harder than it needs to be (or maybe I'm lacking some information here). You can't use an IntentService, because that will shut down automatically the first time an AsyncTask takes over and let's the onHandleIntent callback complete.
You need to setup your own thread and event loop on the service:
public class AsyncService extends Service {
private static final String TAG = AsyncService.class.getSimpleName();
private class LooperThread extends Thread {
public Handler threadHandler = null;
public void run() {
Looper.prepare();
this.threadHandler = new Handler();
Looper.loop();
}
}
private LooperThread serviceThread = null;
private Handler serviceThreadHandler = null;
#Override
// This happens on the UI thread
public void onCreate() {
super.onCreate();
}
#Override
// This happens on the UI thread
public int onStartCommand(Intent intent, int flags, int startId) {
this.serviceThread = new LooperThread();
this.serviceThread.start();
while(this.serviceThread.threadHandler == null) {
Log.d(TAG, "Waiting for service thread to start...");
}
this.serviceThreadHandler = this.serviceThread.threadHandler;
this.serviceThreadHandler.post(new Runnable() {
#Override
public void run() {
doTheFirstThingOnTheServiceThread();
}
});
return Service.START_STICKY;
}
// doTheFirstThingOnTheServiceThread
}
No you need to make sure that each time an AsyncTask returns to the UI thread, you end up in your service thread instead:
// This happens on the serviceThread
private void doTheFirstThingOnTheServiceThread() {
// do some stuff
// here we can reuse a class that performs some work on an AsyncTask
ExistingClassWithAsyncOperation someUsefullObject = new ExistingClassWithAsyncOperation();
// the existing class performs some work on an AsyncTask and reports back via an observer interface
someUsefullObject.setOnOperationCompleteListener(new OnOperationCompleteListener() {
#Override
// This happens on the UI thread (due to an ``AsyncTask`` in someUsefullObject ending)
public void onOperationComplete() {
serviceThreadHandler.post(new Runnable() {
#Override
public void run() {
doTheSecondThingOnTheServiceThread();
}
});
}
}
someUsefulObject.performOperation();
}
// This happens on the serviceThread
private void doTheSecondThingOnTheServiceThread() {
// continue working on the serviceThread
}
So, this works for me. I'd be delighted to see a simpler solution for this. Note that the solution requires the service to know that is will be called back by the ExistingClassWithAsyncOperation on the UI thread. I don't particularly like this dependency, but don't know how to do better right now. However, I don't have to rewrite a lot of existing classes that perform asynchronous operations using AsyncTask.

Android: posting message to HandlerThread makes UI thread unresponsive or gives IllegalStateException

I am trying to make use of HandlerThread in android and am ending up with either a situation in which the UI thread is not responding anymore, or a strange IllegalStateException. I want to give you a minimal example of my problem.
I have a class DataManager which instantiates a worker thread on creation:
public class DataManager
{
private final HandlerThread loaderThread = new HandlerThread( "Worker" );
private final Producer loader;
Inside of this class I have my Handler defined:
private static class Producer extends Handler
{
public Producer( Looper looper )
{
super( looper );
}
#Override
public void handleMessage( Message msg )
{
msg.recycle();
}
}
The constructor of my DataManager runs the worker thread and associates the handler with the thread's looper:
public DataManager()
{
loaderThread.start();
this.loader = new Producer( loaderThread.getLooper() );
}
Before DataManager is destroyed, it stops the thread and waits for it to finish. Actually I believe this part is not relevant to my problem, because my DataManager instance is definitely alive all the time:
#Override
protected void finalize() throws Throwable
{
loaderThread.quit();
loaderThread.join();
super.finalize();
}
Finally, I have doSomething method, which simply posts a message to the worker thread:
public void doSomething()
{
Message msg = Message.obtain();
loader.sendMessage( msg );
}
Now I'm instantiating the DataManager from inside of a custom view on the UI thread. When the view is about to paint itself using onDraw it calls doSomething on the DataManager. The further behavior depends on whether an AsyncTask is currently running in background or not:
If it is running, than the UI thread is becoming unresponsive form this moment on.
Otherwise, I get an IllegalStateException, thrown from within a subroutine of Looper.loop of the UI thread, saying:
IllegalStateException: The specified message queue synchronization barrier token has not been posted or has already been removed.
Google gives absolutely no results for this message. I've been reading documentation and searching for similar problems for a few hours now and still have no idea what I might be doing wrong. Any ideas?
Got it. Obsiously the situation about recycling messages is this:
If you send it to a handler, the handler/looper will recycle it for you.
So one must not recycle the message within handleMessage.

Android ANRs from code running in a Handler?

A game I wrote some time ago has a problem with ANRs, and debugging suggests they're down to HTTP requests taking a long time (and thus causing the ANR).
I'd thought that by assigning the HTTP code into a Runnable called from within a Handler, I'd could avoid the ANR - but it seems this isn't the case?
The stack dumps suggest the runnable/handler code is still running within the 'Main' thread and thus still causes ANRs??
The task it's doing is asynchronous (uploading highscores and achievements) and so can be started and left to it's own devices entirely - what is the best way to implement this so that ANRs aren't going to become a problem?
One topic suggested that the Handler should be created in the Application class and not within the Game's Activity - but I can't find any detail on the differences between those cases??
All ideas greatly apprec.
p.s. extending this to ask - I assume an ANR relating to HTTP comes down to the phone being out-of-service/network/WiFi, because I've set a SHORT timeout for these requests (they're non-essential and can be retried later!?)
A Handler will execute code / handle messages per default (any constructor without Looper e.g. new Handler()) in the current thread. That is in almost every case the main thread. If you want it to execute in a different thread you have to tell it which Looper thread it should use.
Android has a utility class called HandlerThread that creates a Thread with a Looper.
Short example:
public class MyActivity extends Activity {
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
HandlerThread handlerThread = new HandlerThread("background-handler");
handlerThread.start();
Looper looper = handlerThread.getLooper();
mHandler = new Handler(looper);
mHandler.post(new Runnable() {
public void run() {
// code executed in handlerThread
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
// stops the HandlerThread
mHandler.getLooper().quit();
}
}
In case your task needs only a some information and does not need to report back, I'd go with an IntentService. Those don't go mad if your Activity-lifecycle recreates the Activity.
You would create a small Service in it's own file
public class SaveService extends IntentService {
public SaveService() {
super("SaveService");
}
#Override
protected void onHandleIntent(Intent intent) {
if ("com.example.action.SAVE".equals(intent.getAction())) {
String player = intent.getStringExtra("com.example.player");
int score = intent.getIntExtra("com.example.score", -1);
magicHttpSave(player, score); // assuming there is an implementation here
}
}
}
Add it to the AndroidManifest.xml
<application ....
<service android:name=".SaveService" />
</application>
And in your code start it with
Intent intent = new Intent(this /* context */, SaveService.class);
intent.setAction("com.example.action.SAVE");
intent.putExtra("com.example.player", "John123");
intent.putExtra("com.example.score", 5123);
startService(intent);
IntentService#onHandleIntent() runs on a background thread already so you don't have to bother about that.
Your Handler runs on the main thread. That is what causes ANR.
Even if you create it in the Application, by default (no parameters given to Handler) will be created on the main thread. You have to create a Looper, with its own Thread. See here.
A Handler initialized with your own Looper, that is a viable option to solve ANR...
A more simple alternative solution can be, if you place your async network operation into an AsyncTask. A simple approach is to place the AsyncTask into your Activity. A somewhat more complex could be to create a Service (holder for non-ui related functionality), that does the communication, and cleans itself from memory, once the communication is over...
I'd use AsyncTask and place it into the Activity / fire it up from the Activity...
Finally, HERE you can find a nice tutorial on threads in android.

How to keep thread alive without blocking app?

In order to execute some IO operations in my app I wrote a thread, there's nothing on its run method but it has several other methods, like void write(String filename, String data) and void create(String filename), all of which work like a charm. My question is, I used to think this thread was running on the background or something like this but since after removing the .run() statement on my main activity calling said methods still works, how can I have a thread running and waiting for a message from the activity without blocking the app? And second question, since the methods are still working does it mean they are being executed on the main UI thread when I call them from my main activity?
You should use the start() method, instead of the run().
With run() you are running the given Runnable in the calling thread.
With start() you are starting a new thread that handles this Runnable
For the methods to run on the said thread you will have to have to call your methods from the thread and not from any other thread.
class WorkerThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Then use WorkerThread.mHandler.postRunnable or sendMesssage for the work to be done on another thread.
In order to make a que for processing stuff when delivered, you need to make use of android's native stuff which is the best option available:
HandlerThread
Looper
Handler
Message
For examples, read this and this.

Specifics on using Looper.prepare() in Android

I'm having a bit of trouble understanding how to use the Looper prepare()/loop()/quit() logic.
I have three threads: one is the UI thread, one is a game logic thread and the last is a network communication thread (a background thread, lives only while being used).
The game thread has many dependencies on the results of the network calls, so I wanted to spin the network thread off of the game thread and have a Handler post the result back.
Of course, since the UI thread is not involved I need to call Looper.prepare()... somewhere. I thought it should be called in the game thread, but I can't do that because loop() takes it over.
How do I go about posting back to the game thread from network thread with my handler?
What's going on is that once you call Looper.prepare() followed by Looper.loop() on a Thread, all that Thread will ever do is service its MessageQueue until someone calls quit() on its Looper.
The other thing to realize is that, by default, when a Handler is instantiated, it's code will always execute on the Thread it was created on
What you should do is create a new Thread and in run() call Looper.prepare(), setup any Handlers, and then call Looper.loop().
Bearing these things in mind here is the basic pattern I use a lot of places. Also, there's a good chance you should just be using AsyncTask instead.
public class NetworkThread extends Thread {
private Handler mHandler;
private Handler mCallback;
private int QUIT = 0;
private int DOWNLOAD_FILE = 1;
public NetworkThread(Handler onDownloaded) {
mCallback = onDownloaded;
}
public void run() {
Looper.prepare();
mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
// things that this thread should do
case QUIT:
Looper.myLooper().quit();
break;
case DOWNLOAD_FILE:
// download the file
mCallback.sendMessage(/*result is ready*/);
}
}
}
Looper.loop();
}
public void stopWorking() {
// construct message to send to mHandler that causes it to call
// Looper.myLooper().quit
}
public void downloadFile(String url) {
// construct a message to send to mHandler that will cause it to
// download the file
}
}
Could you tell some examples for what you are using your network thread? I think you can solve your problems without using Looper.
You can use ASyncTask to perform background task that may update some values in your UI thread. If user has to wait until background operation will be finished, you can show ProgressDialog and block application in OnPreExecute method, and then hide it in onPostExecute.
As I said, please describe more your needs and target which you want to achieve.

Categories

Resources