22

Hi I want to make Toast available to me no-matter-what and available from any thread whenever I like within my application. So to do this I extended the Activity class:

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Toast;

public class MyActivity extends Activity{
    private Handler mHandler;

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

    private class ToastRunnable implements Runnable {
        String mText;

        public ToastRunnable(String text) {
            mText = text;
        }

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

    public void doToast(String msg) {
        mHandler.post(new ToastRunnable(msg));
    }
}

so that all Activity classes in my app are now simply

public class AppMain extends MyActivity {
   //blah
}

what I expected to be able to do (in a worker thread) was this:

try{
   MyActivity me = (MyActivity) Looper.getMainLooper().getThread();
   me.doToast("Hello World");
}
catch (Exception ex){
   Log.e("oh dear", ex.getMessage());
}

and so long as the Activity was a "MyActivity" it should work - but the problem is ---> the Looper.getMainLooper().getThread(); isn't returning the MyActivity to me and it's making me cry - what am I doing wrong?

: EDIT :

some background to explain "why" I am stuck with this type of implmentation.

I need to be able to confirm to the user that a "HTTP POST" event has been a success. Now. If the User clicks "OK" on the UI Form it MAY or MAY NOT have internet at that time.. If it has Internet - all well and good - it posts the form via HTTP POST all well and good.. but if there is NO Internet most (99.999% of Android apps lame /pathetic / mewling at this, and basically offer the user no plan "b" assuming that at all times the internet is there - when it is NOT)

My App will not "go lame (as I call it)" - it does have a plan "b" instead it "Queues" the post event and retries every x minutes.. now this is a silent thread in the background.. I have plenty of user interaction all over the app I don't know where the user will "be" but eventually when the HTTP POST that queue/retries/queue/retries returns "! Success! " I want to Toast that as a message to the user (EG: "your form was sent")

conners
  • 1,420
  • 4
  • 18
  • 28

7 Answers7

60

What's wrong with runOnUiThread?

http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)

activity.runOnUiThread(new Runnable() {
    public void run() {
        Toast.makeText(activity, "Hello, world!", Toast.LENGTH_SHORT).show();
    }
});
Rawkode
  • 21,990
  • 5
  • 38
  • 45
  • nope, I want to post `Toast` messages to the user, `runOnUiThread` accepts a `Runnable Action` which `Toast` does not work with – conners Dec 06 '12 at 15:35
  • there is no `activity` because there is no `context` – conners Dec 06 '12 at 15:45
  • 1
    Why not use `getApplicationContext()` ? – Rawkode Dec 06 '12 at 15:46
  • 1
    Inconsequential but untrue. There's _always_ an application context. Whether you can get to it is a different story. – Brian Dupuis Dec 06 '12 at 16:12
  • 1
    So why not use a BroadcastReceiver and have your `MyActivity` implementation listen for it? – Rawkode Dec 06 '12 at 16:18
  • 4
    @Rawkode I found this article http://www.helloandroid.com/tutorials/broadcast-receiver-activity which basically does the job I need.. simple, elegant – conners Dec 07 '12 at 21:05
  • @conners I changed my app to pass the context in the constructor as the article attempts to invoke ApplicationObject.applicationContext, which is not available in the library class – Brian Leeming Nov 14 '13 at 16:23
6

use below code. create activity object which contains your activity instance..

activity.runOnUiThread(new Runnable() {
  public void run() {
    Toast.makeText(activity.getApplicationContext(),"Toast text",Toast.LENGTH_SHORT).show();
  }
);
sabadow
  • 5,095
  • 3
  • 34
  • 51
AAnkit
  • 27,299
  • 12
  • 60
  • 71
  • I'm in a `worker` thread there is no `activity` (it's not passable as a parameter because the worker is perpetually trying to connect to the server to send a message.. and the Activity calls in then closes itself.. I need to confirm from the server and acknowledge the event to the user as soon as it happens. All I need is Android to cough up the current ui thread as an Activity. – conners Dec 06 '12 at 15:44
  • 1
    If you are unable to pass an activity reference to the worker thread, it is obvious that your thread design is the root of the problem. You should be able to pass the reference when creating the thread, rather than implying the thread materialized on its own. – Abandoned Cart Nov 10 '14 at 18:25
4

This will allow you to display the message without needing to rely on the context to launch the toast, only to reference when displaying the message itself.

runOnUiThread was not working from an OpenGL View thread and this was the solution. Hope it helps.

private Handler handler = new Handler();
handler.post(new Runnable() {
    public void run() {
        Toast.makeText(activity, "Hello, world!", Toast.LENGTH_SHORT).show();
    }
});
Abandoned Cart
  • 4,512
  • 1
  • 34
  • 41
2

You can't just cast the result of getThread() to an instance of your MyActivity base class. getThread() returns a Thread which has nothing to do with Activity.

There's no great -- read: clean -- way of doing what you want to do. At some point, your "worker thread" abstraction will have to have a reference to something that can create a Toast for you. Saving off some static variable containing a reference to your Activity subclass simply to be able to shortcut Toast creation is a recipe for memory leaks and pain.

Brian Dupuis
  • 8,136
  • 3
  • 25
  • 29
  • I am not using a static class it's a base extension to `Activity` to include a `Handler`.. All I want Android to do is cough up the Current Activity.. – conners Dec 06 '12 at 15:47
  • 1
    @conners: That does not change the fact that `getThread()` returns a `Thread`, which is not an `Activity`, let alone `MyActivity`. – CommonsWare Dec 06 '12 at 15:49
  • I agree but there has to be a way, as far as Android lets me in the SDK the `Activity` is the front face of the UI Thread.. I am not sure why there isn't a `Looper.getMainLooper().getThread().getActivity() / .getContext()` to be honest.. I was expecting there to be one. most solutions require you to pass the context as a parameter - but what if you don't know what context it is or what if it's a form close event that called the worker (as in my case)? – conners Dec 06 '12 at 15:58
  • I don't know what a "form close event" is. Context management is a huge part of Android development and short-circuiting it is almost always unwise. Your problem boils down to determining how to acquire an appropriate `Context` object where you need it. – Brian Dupuis Dec 06 '12 at 16:06
  • From your edit, you could likely use an `AsyncTask` to perform your "retry ad infinitum" routine and show your `Toast` in `onPostExecute()`. Or a `Service` and `BroadcastReceiver`. A raw `Thread` is not the best solution to your problem. – Brian Dupuis Dec 06 '12 at 16:09
  • a form close event is a user operation where a user enters data and confirms that he's done with that form. Usually it's got an "OK" button on it. when you click "Add Comment" to use this web page and add a comment you are doing a "form close event" – conners Dec 06 '12 at 16:10
1

Why don't you send an intent that is captured by a BroadCastReceiver, then the broadcast receiver can create a notification in the notification tray. It's not a toast, but its a way to inform the user that his post has been successful.

fernandohur
  • 7,014
  • 11
  • 48
  • 86
0

If it's within your own activity, why can't you just call doToast()?

Karakuri
  • 38,365
  • 12
  • 84
  • 104
0

if you have the context with you, you can call the ui thread like this from non activity class.

((Activity)context).runOnUiThread(new Runnable() {
    public void run() {
        // things need to work on ui thread
    }
});