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")
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();
}
});
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();
}
);
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();
}
});
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.
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.
If it's within your own activity, why can't you just call doToast()?
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
}
});
Related
I've to develop a widget (AppWidget) that contains a button.
When the button is pressed an http request is done and, if the request is successful, the widget notify the fact through a Toast.
Since the http request can lock the UI, I run it in a thread.
All it's working fine (I can see "All right" in my logat), except for the Toast notification.
What am I doing wrong?
In the AppWidgetProvider subclass:
#Override
public void onReceive(Context c, Intent intent){
super.onReceive(c, intent);
if(intent.getAction().equals(CLICK_ACTION)){
performRequest(c);
}
}
(I think onUpdate method is not necessary for the question)
private void performRequest(final Context c){
new Thread(new Runnable(){
public void run() {
try {
if(Http.get(URL).contains(SUCCESS)){
Toast.makeText(c, "All right", Toast.LENGTH_SHORT).show();
Log.i("Widget","All right");
}
} catch (Exception e) {
e.printStackTrace();
}}}).start();
}
I think that the problem is the Context argument of performRequest, but if I remove the final keyword, I have errors.
Thanks in advice.
[EDIT]
I "solved" the problem in this way:
-I declare Toast t as a global variable;
-Inside the onReceive method I initialize it with Toast.makeText(Context,String,int)
-When I need to show it, I simply call t.show()
I hope that there are better solutions.
Since the http request can lock the UI, I run it in a thread. All it's working fine (I can view "All right" in my logat), except for the Toast notification. What am I doing wrong?
The problem is you cannot alter the UI from another Thread. You must pass this message back to the main Thread in order to display your Toast.
How can I do it?
There are a lot of different ways to do this, try saving the Context in onReceive() as a field variable:
Context context;
#Override
public void onReceive(Context c, Intent intent){
super.onReceive(c, intent);
context = c;
...
Next create a simple method in your Activity:
public void httpGetSucceeded() {
Toast.makeText(context, "All right", Toast.LENGTH_SHORT).show();
Log.i("Widget","All right");
}
Now call this inside your Runnable:
try {
if(Http.get(URL).contains(SUCCESS)){
httpGetSucceeded();
}
} //etc
Better yet, why don't you have a handler that dispatches a message after the thread completes its task. When you receive that message in your broadcast receiver just consume the message and display the toast by using the post runnable method from the Handler object.
Because the handler will belong to your broadcast receiver, you should be running on the UI thread when you call the post for the runnable.
Ref
http://developer.android.com/reference/android/os/Handler.html
http://developer.android.com/reference/android/os/Handler.html#post(java.lang.Runnable)
Easiest way is to call
activity.runOnUiThread(new Runnable() {
public void run() {
// launch toast here
}
});
Or, given a specific widget:
view.post(new Runnable() {
public void run() {
// launch toast here
}
});
I have an android application with different activities and they all pull data from a web source. This is done by implementing Runnable and creating a thread with the activity as object. The basic class looks like this:
public ActivityX extends Activity implements Runnable {
#Override
public onResume() {
super.onResume();
Thread someThread = new Thread(this);
someThread.start();
}
#Override
public run() {
try {
// pull web content
}
catch(TimeOutException e) {
// >>> create dialog here <<<
// go back to another activity
}
}
}
I tried to create a dialog helper class with a static method that returns the timeout dialog and then call show() like this:
HelperClass.getTimeOutDialog().show();
but the problem is, I can't call it from inside the run() method, as it's in a different thread. If I try to, I will get a runtime exception stating:
Can't create handler inside thread that has not called Looper.prepare()
I need to do this dialog for nearly a dozen of activities and I really want to get around using a Handler objects and sending a message to call the dialog every time. Isn't there an easier way to do this? I just can't think of any right now unfortunately.
My code would look something like this:
handler.handleEmptyMessage(1);
This is to call the handler. And the following would handle the message:
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if(msg.what == 1) {
// show dialog here
}
}
};
Cheers
#Override
public run() {
try {
// pull web content
}
catch(TimeOutException e) {
runOnUiThread(new Runnable(){
#Override
public void run() {
// >>> create dialog here <<<
// go back to another activity
}
}
}
}
Try the one above if you don't want to use Handler.
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if(msg.what == 1) {
// show dialog here
}
}
};
Is this code a part of your activity and not in a thread? If it is a part of your non Ui thread, it would give you the error message. Make sure the handler instance is created in your UI thread because a handler contains an implicit reference to the thread they get created in.
I am trying to update my UI in FirstActivity when I receive a notification but is confused by runOnUiThread , Runnable and Handler. Here is what I have: I am running FirstActivity and NotificationService. When NotificationService reeives a notification, it will update FirstActivity UI.
I also have another service AlarmService running.
First Activity
#Override
public void onResume() {
super.onResume();
//some other code for alarm service
}
NotificationService
//on receiving notification
private void showNotification(String text) {
//Get activity
Class<?> activityClass = null;
try {
activityClass = Class.forName("com.pakage.FirstActivity");
contextActivity = (Activity) activityClass.newInstance();
//Update UI on FirstActivity not working
contextActivity.runOnUiThread(new Runnable() {
public void run()
{
Looper.prepare();
TextView tv = (TextView ) contextActivity.findViewById(R.id.notifyTest);
Looper.loop();
}
});
} catch (Exception e) {
e.printStackTrace();
}
//Shows the notification
Notification n = new Notification();
//... etc
}
I keep getting looper.prepare error. Do I need to put extra codes in my FirstActivity?
My 1st instinct is that you should instead have the Activity bind to your service and handle the UI update on its side instead of the Service directly modifying the Activity.
See more info here:
http://developer.android.com/reference/android/app/Service.html#LocalServiceSample
And an example here:
Example: Communication between Activity and Service using Messaging
I've always just had the service fire off a Broadcast and then in my Activity I have a BroadcastReciever listening for the Broadcast. It's an approach that is much simpler than the one you outlined above.
I have no idea why you are putting a Looper in
contextActivity.runOnUiThread(new Runnable() {
public void run()
{
Looper.prepare();
TextView tv = (TextView ) contextActivity.findViewById(R.id.notifyTest);
Looper.loop();
}
});
because the UI (main) thread already has a Looper/Handler etc..
Even if it did work Looper.loop() is going to block and since you are running it on the UI thread, it will block the UI thread which is not what you want.
What you really want to do is
contextActivity.runOnUiThread(new Runnable() {
public void run()
{
TextView tv = (TextView ) contextActivity.findViewById(R.id.notifyTest);
tv.setText("do something that must be on UI thread") // or whatever
}
});
You don't really need to do all this fancy stuff to get the Activity
activityClass = Class.forName("com.pakage.FirstActivity");
contextActivity = (Activity) activityClass.newInstance();
assuming the Service and Activity are both running in the same process you can just save a reference to the Activity but be careful to update the reference when the Activity gets destroyed.
I need to display a message to the user "Communicating to the Server...Please wait for few seconds" when a call to a webservice is made. Currently I'm using Toast.makeText to display the message. For some reason, I don't see the message pop-up. But interestingly when I comment the web service method call, I see the Toast message.
Toast.makeText(this, "Communicating to the Server...Please wait for few seconds",
Toast.LENGTH_SHORT).show();
//webservice code goes here...
Or any other alternative to satisfy this requirement is also fine.
Have you looked at using AysncTask. Using AsyncTask you can show a dialog with your message on onPreExecute().
Do NOT mix UI code and network code. See: http://developer.android.com/resources/articles/painless-threading.html
You can use AsyncTask to run your service and show Toast in onPreExecute.
Or you can use normal Thread but, you'll need to use Handler. Here is how:
class MyActivity extends Activity
{
final Handler mHandler;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(...);
mHandler = new Handler();
...
}
void showToast(final String text)
{
mHandler.post(new Runnable()
{
#Override
public void run()
{
Toast.makeText(MyActivity.this, text, Toast.LENGTH_LONG).show();
}
});
}
class MyThread implements Runnable
{
#Override
public void run()
{
showToast("your custom text");
//your service code
}
}
}
And here is how you start the thread:
Thread thread = new Thread(new MyThread());
thread.run();
The problem is that the UI thread is blocked as soon as you make the blocking web service call, so it never updates with the toast message. By the time it returns, the time for toast message has expired.
Run your web service call in a thread, using AsyncTask, or just create a thread like,
new Thread(new Runnable() {
public void run() {
// WS call here
}
}).start();
Take care that if you create your own thread, you can only update the UI from the UI thread, so you'll need to use Handler.post() or sendMessage() to run the UI update on the UI thread.
http://developer.android.com/reference/android/os/AsyncTask.html
http://developer.android.com/reference/android/os/Handler.html
In my OnCreate method I have created a thread that listens to incoming message!
In OnCreate() {
//Some code
myThread = new Thread() {
#Override
public void run() {
receiveMyMessages();
}
};
myThread.start();
// Some code related to sending out by pressing button etc.
}
Then, receiveMyMessage() functions…
Public void receiveMyMessage()
{
//Receive the message and put it in String str;
str = receivedAllTheMessage();
// << here I want to be able to update this str to a textView. But, How?
}
I checked this article but it did not work for me, no luck!
Any updates to the UI in an Android application must happen in the UI thread. If you spawn a thread to do work in the background you must marshal the results back to the UI thread before you touch a View. You can use the Handler class to perform the marshaling:
public class TestActivity extends Activity {
// Handler gets created on the UI-thread
private Handler mHandler = new Handler();
// This gets executed in a non-UI thread:
public void receiveMyMessage() {
final String str = receivedAllTheMessage();
mHandler.post(new Runnable() {
#Override
public void run() {
// This gets executed on the UI thread so it can safely modify Views
mTextView.setText(str);
}
});
}
The AsyncTask class simplifies a lot of the details for you and is also something you could look into. For example, I believe it provides you with a thread pool to help mitigate some of the cost associated with spawning a new thread each time you want to do background work.
Android supports message-passing concurrency using handlers and sendMessage(msg). (It is also possible to use handlers for shared-memory concurrency.) One tip is to call thread.setDaemon(true) if you wish the thread to die when the app dies. The other tip is to have only one handler and use message.what and a switch statement in the message handler to route messages.
Code and Code