How to display Toast from a Service after main Activity finishes?
Asked Answered
I

5

15

UPDATE: I don't agree that this is a duplicate - because I am seeking for a way to exit the main app and still show a Toast from the service.

In a very simple test app I have 2 buttons:

screenshot

Clicking any of the buttons will run a service with a corresponding action string ("open" or "flash") -

OpenActivity.java:

public class OpenActivity extends Activity {
    private Intent mServiceIntent;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_open);
        mServiceIntent = new Intent(this, RegionService.class);
   }

    public void openCar(View v) {
        mServiceIntent.setAction("open");
        startService(mServiceIntent);
    }

RegionService.java:

public class RegionService extends IntentService {
    private static final String TAG = "RegionService";

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "Received an intent: " + intent);
        String action = intent.getAction();
        Log.d(TAG, "Received an action: " + action);

        if(action.equals("open")) {
            Toast.makeText(this, 
                    getString(R.string.car_opened), 
                    Toast.LENGTH_SHORT).show();
        } 

Unfortunately my app crashes with:

D/RegionService(24506): Received an intent: Intent { act=open cmp=de.afarber.mynotification/.RegionService }

D/RegionService(24506): Received an action: open

W/MessageQueue(24506): Handler (android.os.Handler) {422768a8} sending message to a Handler on a dead thread
W/MessageQueue(24506): java.lang.RuntimeException: Handler (android.os.Handler) {422768a8} sending message to a Handler on a dead thread
W/MessageQueue(24506):  at android.os.MessageQueue.enqueueMessage(MessageQueue.java:320)
W/MessageQueue(24506):  at android.os.Handler.enqueueMessage(Handler.java:626)
W/MessageQueue(24506):  at android.os.Handler.sendMessageAtTime(Handler.java:595)
W/MessageQueue(24506):  at android.os.Handler.sendMessageDelayed(Handler.java:566)
W/MessageQueue(24506):  at android.os.Handler.post(Handler.java:326)
W/MessageQueue(24506):  at android.widget.Toast$TN.hide(Toast.java:370)
W/MessageQueue(24506):  at android.app.ITransientNotification$Stub.onTransact(ITransientNotification.java:54)
W/MessageQueue(24506):  at android.os.Binder.execTransact(Binder.java:412)
W/MessageQueue(24506):  at dalvik.system.NativeStart.run(Native Method)

Being an Android programming newbie I wonder How to display a Toast from Service in a correct way?

I think I've already seen Toasts at Android Home (i.e. there was no Activity on the device screen and still there were Toasts).

My background: I would like to monitor a beacon device from my service and show some text Toasts - even when my app has been closed.

Indre answered 8/12, 2014 at 12:34 Comment(3)
jjoe64.com/2011/09/show-toast-notification-from-service.htmlInterosculate
possible duplicate of Toast created in an IntentService never goes awayCuckooflower
I don't agree that this is a duplicate - because in my case I'd like to exit the main app and still show a Toast from the service.Indre
T
44

OnHandleIntent will run in a differant Thread so you are showing Toast in a thread which is not allowed in android

so change your code like this

Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {

    @Override
    public void run() {
         Toast.makeText(getApplicationContext(), 
                       getString(R.string.car_opened), 
                       Toast.LENGTH_SHORT).show();              
    }
});

From this dead thread in service

IntentService will create a thread to handle the new intent, and terminated it immediately once the task has done. So, the Toast will be out of controlled by a dead thread.

You should see some exceptions in the console when the toast showing on the screen.

Timer answered 8/12, 2014 at 12:38 Comment(4)
I've tried putting your code excerpt into onHandleIntent - but it does not compile because of this being a Runnable.Indre
@AlexanderFarber place YourService.this instead of this or chaneg to getApplicationContext()Timer
If anyone still have trouble getting the Toast displayed, just override onCreate and initialize the Handler here instead of initializing in onHandleIntent(). Only call the post method in onHandleIntent().Assimilation
This is not working on android 13Grappa
D
5

An IntentService has a few limitations:

It can't interact directly with your user interface. To put its results in the UI, you have to send them to an Activity.

Everything is happening in the background thread and not on the UI thread, so you need a different way as shown below:

@Override 
public void onCreate() { 
    super.onCreate(); 
    mHandler = new Handler(); 
} 

@Override 
protected void onHandleIntent(Intent intent) {
    mHandler.post(new Runnable() {            
        @Override 
        public void run() { 
            Toast.makeText(MyIntentService.this, "Hello Toast!", Toast.LENGTH_LONG).show();                
        } 
    }); 
} 

Source: Toast created in an IntentService never goes away

Deploy answered 8/12, 2014 at 12:38 Comment(0)
U
2

OnHandleIntent is called on a background thread, and any attempt to touch the UI will result in a crash. Use an Handler to post a Runnable on the UI Thread to show your toast.

private class MyRunnable implements Runnable {

   final int mTextId = -1; 
   final Context mContext;
   public MyRunnable(Context c, int textId) {
       mTextId = textId;
       mContext = c;
   }

   @Override
   public void run() {
       Toast.makeText(mContext, 
           getString(mTextId), 
           Toast.LENGTH_SHORT).show();             
    }
}

   Handler handler = new Handler();
   handler.post(new MyRunnable(this, R.string.car_opened));
Utu answered 8/12, 2014 at 12:37 Comment(2)
Why dont you give all these code snippets a try? :)Deploy
Of course I try the snippets, but sometimes bugs/crashes are not immediately visible (especially when something runs in background) - that is why it is good to ask.Indre
E
1

use the following code:

  runOnUiThread(new Runnable(){
     public void run() {
          // UI code goes here
     }
    });
Embody answered 8/12, 2014 at 12:36 Comment(5)
yes it shows even when service is running in backgroundEmbody
Activity instance can be passed to a serviceEmbody
That is true, you will also leak the instance once you rotate the screen. You don't need the activity to access the main thread.Octavie
It works but toast can also be shown using service context directly.Embody
You can use any context to create the toast BUT the point is you have to call Toast.show() on UI thread for it to work properly.Octavie
S
0

This problem because of not running the Toast from the main_thread, and to overcome that, when creating the Activity save it's context from onCreate() method:

public static Context ctx;

// the method responsible for running the MainActivity
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ctx = this;
}

then, in the service add a handler and run a thread from it(as the Handler is executed through the main Thread):

    Handler handler = new Handler(Looper.getMainLooper());
    handler.post(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(OpenActivity.ctx, getString(R.string.car_opened),
                    Toast.LENGTH_SHORT).show();
        }
    });
Stool answered 8/12, 2014 at 12:46 Comment(6)
why is there a need to store this?Deploy
@LittleChild as I don't know the Thread state of his service and don't have an overview of the app structure and threads, unsaving it will mostly work but in some cases won't, So I saved it to make sure that it will certainly run.Stool
You can take a look at my complete app at github.com/afarber/android-newbie/tree/q12/MyNotification/src/…Indre
@AlexanderFarber there isn't a problem in that, not good or bad, but it makes you make sure that you won't go into trouble with serviceintent context.Stool
@MuhammedRefaat You mean just in case the service gets destroyed unexpectedly??Deploy
@LittleChild yes, gets destroyed or anything else happened that can make it stop.Stool

© 2022 - 2024 — McMap. All rights reserved.