android - calling ui thread from worker thread
Asked Answered
P

7

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")

Purpurin answered 6/12, 2012 at 15:29 Comment(0)
N
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();
    }
});
Numbat answered 6/12, 2012 at 15:31 Comment(7)
nope, I want to post Toast messages to the user, runOnUiThread accepts a Runnable Action which Toast does not work withPurpurin
there is no activity because there is no contextPurpurin
Why not use getApplicationContext() ?Numbat
Inconsequential but untrue. There's always an application context. Whether you can get to it is a different story.Waldgrave
So why not use a BroadcastReceiver and have your MyActivity implementation listen for it?Numbat
@Numbat I found this article helloandroid.com/tutorials/broadcast-receiver-activity which basically does the job I need.. simple, elegantPurpurin
@Purpurin 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 classLounging
P
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();
  }
);
Purgative answered 6/12, 2012 at 15:38 Comment(2)
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.Purpurin
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.Templas
T
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();
    }
});
Templas answered 15/11, 2014 at 4:13 Comment(0)
W
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.

Waldgrave answered 6/12, 2012 at 15:42 Comment(6)
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..Purpurin
@conners: That does not change the fact that getThread() returns a Thread, which is not an Activity, let alone MyActivity.Grovel
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)?Purpurin
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.Waldgrave
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.Waldgrave
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"Purpurin
N
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.

Nymph answered 25/5, 2013 at 5:8 Comment(0)
A
0

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

Ascidium answered 6/12, 2012 at 15:44 Comment(0)
K
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
    }
});
Koestler answered 23/3, 2016 at 7:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.