I'm using the tutorial here to develop a Service that is (right now) just running a TimerTask to do System.out.println("tick") every second. My code is exactly like the code on the site, aside from some name changes. Everything works (the Service runs, outputs "tick") if I don't try to pass a String from the Service to the Activity.
What I'm trying to accomplish is to get a TextView in the main Activity to be updated with a String received from the Service. I have an append(String) method working fine that will update the TextView with new text. So in my Service's TimerTask I've added listener.handleMessage("tick") and my Activity implements the listener functionality:
public void handleMessage(String msg) throws RemoteException {
append(msg);
}
When I run the application, System.out shows a "tick", then a stacktrace with the CalledFromWrongThreadException, pointing to the append() method as the source of the problem.
I know there's a few questions about this Exception, but most of them concern Thread and Handler issues; I couldn't find anything about Services. Anyone know if this is possible?
Solution
Extend Runnable:
class MyRunnable implements Runnable {
private String msg;
public MyRunnable(String msg) {
this.msg = msg;
}
public void run() {
appendNewline(msg);
}
}
and replace the callback with a call to global Handler:
public void handleMessage(String msg) throws RemoteException {
handler.post(new MyRunnable(msg));
}
Updates to the ui have to occur on the ui thread and not a background thread (like that of the timer). To update the ui, declare a member variable of type Handler and call the post method, passing a new runnable instance that can update your text view. This is a decent tutorial
Related
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.
I'm building an application that has a widget and a button in the widget launches an IntentService.
The onHandleIntent() runs some code and then raises a toast through a handler.
After I click the button in the widget, I see the toast and I know that onHandleIntent finished.
But when I look in the background services I still see my app there.
As a user, I get very annoyed when apps always run on the background and wasting my precious RAM. My widget doesn't need to run in the background because my widget doesn't update ever (the update rate in the xml is 0).
So how come that the service is still running? How can I stop it?
IntentService code:
public class WidgetCheckService extends IntentService {
private int mAppWidgetId;
Handler mHandler = new Handler();
public SalaryWidgetCheckService(String name) {
super("laceService");
}
public SalaryWidgetCheckService() {
super("laceService");
}
#Override
protected void onHandleIntent(Intent intent) {
// Badass code
mHandler.post(new DisplayToast(getString(R.string.widget_service_check_in_success_toast_text).replace("LACE", lace)));
else mHandler.post(new DisplayToast(getString(R.string.widget_service_check_out_success_toast_text).replace("LACE", lace)));
}
private class DisplayToast implements Runnable{
String mText;
public DisplayToast(String text){
mText = text;
}
public void run(){
Toast.makeText(WidgetCheckService.this, mText, Toast.LENGTH_SHORT).show();
}
}
}
Thanks, Elad.
But when I look in the background services I still see my app there.
Your process is not automatically terminated when the service ends. Android will keep your process running until it needs to free up the RAM for other apps, just in case it happens to need to run something from your app again. This is no different than any other app on Android.
In my OnCreate method I have created a thread that listens to incoming message!
In OnCreate() {
//Some code
myThread = new Thread() {
#Override
public void run() {
receiveMyMessages();
}
};
myThread.start();
// Some code related to sending out by pressing button etc.
}
Then, receiveMyMessage() functions…
Public void receiveMyMessage()
{
//Receive the message and put it in String str;
str = receivedAllTheMessage();
// << here I want to be able to update this str to a textView. But, How?
}
I checked this article but it did not work for me, no luck!
Any updates to the UI in an Android application must happen in the UI thread. If you spawn a thread to do work in the background you must marshal the results back to the UI thread before you touch a View. You can use the Handler class to perform the marshaling:
public class TestActivity extends Activity {
// Handler gets created on the UI-thread
private Handler mHandler = new Handler();
// This gets executed in a non-UI thread:
public void receiveMyMessage() {
final String str = receivedAllTheMessage();
mHandler.post(new Runnable() {
#Override
public void run() {
// This gets executed on the UI thread so it can safely modify Views
mTextView.setText(str);
}
});
}
The AsyncTask class simplifies a lot of the details for you and is also something you could look into. For example, I believe it provides you with a thread pool to help mitigate some of the cost associated with spawning a new thread each time you want to do background work.
Android supports message-passing concurrency using handlers and sendMessage(msg). (It is also possible to use handlers for shared-memory concurrency.) One tip is to call thread.setDaemon(true) if you wish the thread to die when the app dies. The other tip is to have only one handler and use message.what and a switch statement in the message handler to route messages.
Code and Code
Dear all
I have an android activity and a normal .java class which contains an "onChange" function(the function is called whenever properties of my skype contact are changed)
When I try to call an alertdialog.show() in my onChange function, I got error "Can't create handler inside thread that has not called Looper.prepare()", what should I do to show a message in my activity? thanks in advance
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
From the android documentation :
Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped
Also never ever have UI calls in a (worker) Thread. It is bound to throw exceptions.
I have an app with a two threads - main and data loader. When data loader finishes it posts a Runnable object to the main thread (as described in the DevGuide), but it never gets delivered and run.
Here's the basic code:
class MyApp extends Application
{
public void onCreate()
{
LoaderThread t = new LoaderThread();
t.start();
}
private class LoaderThread extends Thread
{
public void run()
{
SystemClock.sleep(2000);
boolean res = m_handler.post(m_runnable);
if(res)
Log.d(TAG, "Posted Runnable");
}
}
private final Handler m_handler = new Handler();
private final Runnable m_runnable = new Runnable() {
public void run()
{
Log.d(TAG, "Hey, i'm runnable!");
}
}
}
Also it maybe important to note that I ran this code as a unit-test derived from an ApplicationTestCase:
class MyAppTest : public ApplicationTestCase
{
public MyAppTest()
{
super(MyApp.class);
}
public void testLoading()
{
createApplication();
// few asserts follow here...
}
}
So this fails. Runnable never gets run() called, although the log indicates that it has been posted successfully.
I also tried to send simple messages instead of posting runnable (m_handler.sendEmptyMessage(1) for example) - they never get delivered to handler's callback in the main thread.
What do I miss here?
Thanks in advance :)
A Handler requires a Looper in order to work. The Looper provides a message queue required by the Handler.
All instances of Activity have a Looper as one is used to process UI Events, but you can create your instance of Looper elsewhere.
Have a look in your Log output to see if Android is complaining about the absence of a Looper.
If it is, you might be able to fix by add the following to the top of your onCreate() method:
Looper.prepare();
m_handler = new Handler();
Looper.run();
And remove the initialisation of m_handler from later in your code.
Handler only works in an Activity AFAIK. You are attempting to use it in an Application.
An alternative to calling Looper.prepare() is to call new Handler(Looper.getMainLooper()). The problem with calling Looper.prepare() is that it will throw an exception when there is already a looper on your thread. Chances are you are writing code that has to run under different environments and this solution will handle more cases.
See:
AsyncTask and Looper.prepare() error