29

This IntentService I created will show Toasts in onStartCommand() and in onDestroy(), but not in onHandleIntent(). Am I missing something about the limitations of an IntentService?

public class MyService extends IntentService {

private static final String TAG = "MyService";

public MyService(){
    super("MyService");
}

@Override
protected void onHandleIntent(Intent intent) {
    cycle();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); //This happens!
    return super.onStartCommand(intent,flags,startId);
}

@Override
public void onCreate() {
    super.onCreate();

}

@Override
public void onDestroy() {
    Toast.makeText(this, "service stopping", Toast.LENGTH_SHORT).show(); //This happens!
    super.onDestroy();
}

private void cycle(){
      Toast.makeText(this, "cycle done", Toast.LENGTH_SHORT).show();  //This DOESN'T happen!
      Log.d(TAG,"cycle completed"); //This happens!
}
}
Jim G.
  • 15,141
  • 22
  • 103
  • 166
Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • 1
    possible duplicate of [create toast from IntentService](http://stackoverflow.com/questions/3955410/create-toast-from-intentservice) – Jim G. Dec 19 '13 at 17:53

4 Answers4

69

The accepted answer is not correct.

Here is how you can show toast from onHandleIntent():

Create a DisplayToast class:

public class DisplayToast implements Runnable {
    private final Context mContext;
    String mText;

    public DisplayToast(Context mContext, String text){
        this.mContext = mContext;
        mText = text;
    }

    public void run(){
        Toast.makeText(mContext, mText, Toast.LENGTH_SHORT).show();
    }
}

Instantiate a Handler in your service's constructor and call the post method with a DisplayToast object inside.

public class MyService extends IntentService {
Handler mHandler;

public MyService(){
    super("MyService");
    mHandler = new Handler();
}

@Override
protected void onHandleIntent(Intent intent) {
    mHandler.post(new DisplayToast(this, "Hello World!"));

}
}
Community
  • 1
  • 1
Jim G.
  • 15,141
  • 22
  • 103
  • 166
  • 9
    Damn that's clever. I wish there was a tip bot on stack overflow so I could give you dogecoin for an excellent answer. – Hayk Saakian Feb 05 '14 at 00:48
  • IIRC, this is similar to how I solved the problem. The accepted answer is correct, and your answer provides the details of how to get around it (submit it to a handler so it is called on the correct thread. – Tenfour04 Feb 03 '15 at 22:25
  • 1
    Won't this handler bind to the current thread as opposed to the main thread? – Snake Feb 10 '15 at 20:07
  • 3
    Dianne Hackborne didn't say that it was not possible, just said that you should not do UI from a background thread. Passing around context objects is a heavy thing to do, and can lead to leaks. The accepted answer is the best answer, whereas this answer is a workaround. – Sreedevi J Feb 18 '16 at 04:29
  • Why if I move the initialization of the mHandler to onHandleIntent method this code does not work? Why is it have to be declared in the constructor? – Reza Bigdeli Mar 27 '16 at 13:14
  • @RezaBigdeli Because default constructor in Handler means connect to current thread. onHandleIntent() runs on bg thread, handler should be initialized in other method. It could onCreate(), for example. See also answer below by Mannaz. – B-GangsteR Aug 28 '16 at 12:53
25

You should start the Toast on the main thread:

new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
        }
});

This is because otherwise the thread of the IntentService quits before the toast can be send out, causing a IllegalStateException:

java.lang.IllegalStateException: Handler (android.os.Handler) {12345678} sending message to a Handler on a dead thread

whlk
  • 15,487
  • 13
  • 66
  • 96
19

onHandleIntent() is called from a background thread (that is what IntentService is all about), so you shouldn't do UI from there.

hackbod
  • 90,665
  • 16
  • 140
  • 154
  • 2
    That was it, thanks! I created a handler field in onStartCommand() and posted the Toast to the handler and it worked. – Tenfour04 Mar 18 '11 at 02:43
3

Another option is RxJava, e.g.:

private void showToast(final String text) {
    Observable.just(text)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<String>() {
            @Override
            public void call(String s) {
                Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();
            }
        });
}

Caveat: I'm new to Android.

ehnmark
  • 2,656
  • 3
  • 24
  • 20