I have a legacy IntentService
that attempts to use Toast messages to display error messages.1 I'd like the messages to be displayed, and have added code to get them on the correct thread. The simplest change would be to pass in the constructed Toast
object and then display it on the UI thread. However, the Toast only displays if I make it in the posted runnable, not if I pass in a pre-made Toast
.
This works:
@Override
protected void onHandleIntent(Intent intent) {
showToast("Error", Toast.LENGTH_LONG);
}
private void showToast(final String msg, final int duration) {
new Handler(getMainLooper()).post(new Runnable() {
@Override
public void run() {
// Make and show the toast in the posted runnable
Toast.makeText(getApplicationContext(), msg, duration).show();
}
});
}
This doesn't work:
@Override
protected void onHandleIntent(Intent intent) {
// Make the toast here
Toast myToast = Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_LONG);
showToast(myToast);
}
private void showToast(final Toast toast) {
new Handler(getMainLooper()).post(new Runnable() {
@Override
public void run() {
// Show the toast here
toast.show();
}
});
}
In both cases, the context is the application context, and I didn't see anything in the source that would cause one version to work, but the other not. Instead the latter has the same problems as if the Toast was shown directly in the IntentService: "Handler (android.os.Handler) {...} sending message to a Handler on a dead thread", Toast not disappearing, etc.
Why does the Toast have to be made on the main thread instead of just shown there?
1. Legacy = I don't think displaying error messages in Toasts is great UI, and I don't think services displaying messages to users directly is a good idea, but that's the code I was handed and I'd like to make it this little bit better.
A context is the current state of the application/object. The toast function must have a context to know where it has to show the toast.
For toasts, which are short-lived, you can usually use whatever context you want. Typically, you would use the activity context, but application context is fine as well.
Positioning your Toast A standard toast notification appears near the bottom of the screen, centered horizontally. You can change this position with the setGravity(int, int, int) method. This accepts three parameters: a Gravity constant, an x-position offset, and a y-position offset.
Starting from Android Build. VERSION_CODES#R, apps targeting API level Build. VERSION_CODES#R or higher that are in the background will not have custom toast views displayed. If you want to avoid Toast overlapping, you could save the time the last Toast was shown using System.
In the second code that you've posted, the Toast is created in the background thread which has a looper and handler set up (that is the point of IntentService).
The toast uses the current thread's looper to create a handler, but once the IntentService is finished processing the work in onHandleIntent it stops itself (if there aren't other intents to process) - destroying the thread that your Toast's handler is relying on.
line 327: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/widget/Toast.java
Making the toast in the runnable works because at that point, the current thread is the UI thread.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With