I have a thread running in C++, it will call my UI thread's (Java) static method when some condition's satisfied. When the static method was called, I want a Toast to show on my UI. What I have tried are:
1
static void myMethod(){
Toast.makeText(context, "message", Toast.LENGTH_SHORT).show();
(I have a static context reference in global scope)
}
RESULT:
E/AndroidRuntime( 1331): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
2
static void myMethod(){
runOnUiThread(new Runnable(){
public void run(){
Toast.makeText(Context, "message", Toast.LENGTH_SHORT).show();
}
});
RESULT:
Can not compile: Cannot make a static reference to the non-static method runOnUiThread(Runnable) from the type Activity
Can anybody throw some light on this? Many thanks to you.
I think you are calling this method from a different thread than the UI thread and this causes an Exception. I have just tried declaring a static method in my Application class that would do the same as your first code. It worked - but of course only when called from main UI thread.
If you would like to be able to call the static method from a different thread, then you will need to create a handler on the UI thread to display the Toast. Something like this:
private static final int MSG_SHOW_TOAST = 1;
private static Handler messageHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == MSG_SHOW_TOAST) {
String message = (String)msg.obj;
Toast.makeText(App.this, message , Toast.LENGTH_SHORT).show();
}
}
};
private static void displayMessage() {
Message msg = new Message();
msg.what = MSG_SHOW_TOAST;
msg.obj = "Message to show";
messageHandler.sendMessage(msg);
}
The context in my sample is retrieved from App.this, which is the Application class. You can replace this with your Activity, or your static global context.
static Activity thisActivity = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
thisActivity = this;
}
public static void showMsg()
{
Toast.makeText(thisActivity, "message" , Toast.LENGTH_SHORT).show();
}
Try this instead, as described in this post:
public class SampleActivity extends Activity {
/**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
#Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
/**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
*/
private static final Runnable sRunnable = new Runnable() {
#Override
public void run() { }
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(sRunnable, 600000);
// Go back to the previous Activity.
finish();
}
}
I had to use a slightly different method to get the context.
Previously created global Application class:
package com.com.YourAppName;
import android.app.Application;
public class YourAppName_app extends Application {
//declarations, getters, setters, etc...
}
A static method inside your Activity/FragmentActivity where you want the Toast:
public class Home extends FragmentActivity {
static YourAppName_app app;
private static void yourStaticMethod() {
app = ((YourAppName_app)getApplicationContext()); //can also call this in onCreate
Toast.makeText(app, "Your Toast message", Toast.LENGTH_LONG)
.show();
}
Related
Experts,
I would like to plot the chart every 10 seconds. Below code structure should be clear. The problem is:
I cannot use outer class member sDtdChart in inner class PlotHandler, because the inner class is static. What should I do?
If not static, I got warning: This Handler class should be static or leaks might occur.
Thanks.
public class MainActivity extends Activity {
Timer timer = new Timer();
ScatterChart sDtdChart;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timer.schedule(task, 0, 10 * 1000);
}
// plot chart
TimerTask task = new TimerTask() {
public void run() {
Message msg = new Message();
msg.what = 1;
plotHandler.sendMessage(msg);
}
};
Handler plotHandler = new PlotHandler();
private static class PlotHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
plotChart();
break;
}
super.handleMessage(msg);
}
void plotChart() {
// it says non-static field cannot be referenced in a static context
sDtdChart = (ScatterChart) findViewById(R.id.chartDtd);
sDtdChart.setDescription("dtd");
sDtdChart.setData(xxxxx);
sDtdChart.invalidate();
}
}
Have you tried something like
public class MainClass {
public static void main(String[] args){
System.out.println(StaticNestedClass.age);
}
static class StaticNestedClass {
static int age = 10;
}
}
a static nested class cannot refer directly to instance variables or methods defined in its enclosing class: it can use them only through an object reference.
Check this link
After running a code inspection through android studio, it highlight that the MainHandler should be static. I move the class to static but now it complain that
"Non-Static method remainingSecondsChanged(int) cannot be referenced from a static context"
public class CountDownView extends FrameLayout {
private static void remainingSecondsChanged(int newVal) {
mRemainingSecs = newVal;
if (mListener != null) {
mListener.onRemainingSecondsChanged(mRemainingSecs);
}
if (newVal == 0) {
// Countdown has finished.
setVisibility(View.INVISIBLE);
if (mListener != null) {
mRemainingSecondsView.setText(null);
mRemainingSecondsView.setBackgroundResource(R.drawable.bracket_view_finder);
mListener.onCountDownFinished();
}
} else {
Locale locale = getResources().getConfiguration().locale;
String localizedValue = String.format(locale, "%d", newVal);
mRemainingSecondsView.setText(localizedValue);
// Schedule the next remainingSecondsChanged() call in 1 second
mHandler.sendEmptyMessageDelayed(SET_TIMER_TEXT, 1000);
}
}
public void startCountDown(int sec) {
if (sec < 0) {
return;
}
if (sec == 0) {
cancelCountDown();
}
mRemainingSecondsView.setBackgroundResource(R.drawable.bracket_count_down);
setVisibility(View.VISIBLE);
remainingSecondsChanged(sec);
}
private static class MainHandler extends Handler {
#Override
public void handleMessage(Message message) {
if (message.what == SET_TIMER_TEXT) {
remainingSecondsChanged(mRemainingSecs - 1);
}
}
}
private static final MainHandler mHandler = new MainHandler();
}
Any idea how to fix it ?
First... Why's Studio showing that message?
Background
Each Handler is associated with a Thread, and all Handler objects on the same Thread share a common Looper object, where they post and read their messages. Thing is... when these objects are non-static well... non-static inner classes hold an implicit reference to their outer class. So the Handler will hold a reference to your Activity, and if this Handler has a delayed message, your Activity will be unable to be garbage collected until this message is processed.
You can read more about it here.
Solution
As for your problem. The first thing you already did, which is make your Handler a static inner class. Now, create a WeakReference to your outer class (Could be an Activity or I believe in this case, your CountDownView).
Now try changing your Handler to something like this (Instead of Activity you could reference your CountDownView):
private static class MainHandler extends Handler {
private final WeakReference<YourActivity> mActivity;
public MainHandler(YourActivity activity) {
mActivity = new WeakReference<YourActivity>(activity);
}
#Override
public void handleMessage(Message message) {
YourActivity activity = mActivity.get();
if (activity != null) {
if (message.what == SET_TIMER_TEXT) {
activity.remainingSecondsChanged(mRemainingSecs - 1);
}
}
}
}
And instantiate it like this:
// this is a reference to your Activity, or your CountDownView, wherever your method is.
private final MainHandler mHandler = new MainHandler(this);
This StackOverflow post here Explains why the inner classes should be static and it is the pretty much same reason why the code analyzer complaints about it,Suppose If you want the members of the containing class to be accessible from your inner class you can make it non static
I am not android programmer but maybe instead of creating inner class which extends Handler than you can create private field like this:
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
//call your non static method here
}
}
Change the constructor of the MainHandler to receive a callback interface
public MainHandler(Callback cb){
this.mCallBack = cb;
}
Then at handleMessage call the callback interface to perform the method
public void handleMessage(Message message) {
if (message.what == SET_TIMER_TEXT) {
mCallBack.someMethod();1);
}
}
At fragment declare interface
public interface Callback
{
void someMethod();
}
Make your fragment implement it.
private final MainHandler mHandler = new MainHandler(this);
Then at the implementation call
remainingSecondsChanged(mRemainingSecs - 1);
This is not the best way to do it but its the fastest with your current design.
I read the article How to Leak a Context: Handlers & Inner Classes, and now I have got a question. If I pass mHandler as a parameter to another thread to send messages from that thread to the main thread, will it cause memory leaks?
SampleActivity
public class SampleActivity extends Activity {
/**
* Instances of static inner classes do not hold an implicit reference to
* their outer class.
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
#Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Run a thread (authentication, synchronization, etc.)
// Later the user might press the Home button, the Back button, or make a call
new MyThread(mHandler).start();
}
}
MyThread
public class MyThread extends Thread {
private final Handler handler;
public MyThread(Handler handler) {
this.handler = handler;
}
#Override
public void run() {
// A long operation
// I'm done
handler.sendEmptyMessage(1);
}
}
If MyThread is a static or external class, it can't cause leaks. There is no any non-weak references to activity.
It looks ok. I don't see any object holding link to Activity (WeakRef is ok as it can be GCed). I don't see potential leaks here ;)
public class LooperThread extends Thread {
private Handler handler = null;
public Handler getHandler() {
return handler;
}
#Override
public void run() {
Looper.prepare();
handler = new Handler();
Looper.loop();
}
}
class Helper {
private static LooperThread databaseThread = null;
static {
databaseThread = new LooperThread();
databaseThread.start();
}
public void postRunable(Runnable r) {
databaseThread.getHandler().post(r);
databaseThread.getHandler().sendMessage(new Message());
}
}
//ui thread.
class UIActivity extends Activity {
private Helper helper = new Helper();
public void onCreate(Bundle savedInstanceState) {
helper.postRunnable(new Runnable() {
public void run() {
//work asyn,like query from db.
}
});
}
}
sometimes call databaseThread.getHandler().post(r); ,it return null,sometime are not,why this?as usual,handler should be initial by static block.
You are some times getting a null Handler because calling databaseThread.start(); in the the static initializer only ensures that the thread will be started at some point in the future this means thats creating a race condition between the handler getting created inside the new thread and getHandler() being called in the old one. Having a thread with a background looper is a very common pattern in Android so there is a class to help us with this.
First get rid of your LooperThread class and use the SDK's HandlerThread instead.
Your Helper class should now look like
class Helper {
private static final HandlerThread databaseThread;
private static final Handler dbHandler;
static {
databaseThread = new HandlerThread("database thread");
databaseThread.start();
// If you have called HandelerThread#start()
// HandlerThread#getLooper() will block until the looper is initialized and Looping
dbHandler = new Handler(databaseThread.getLooper());
}
public void postRunable(Runnable r) {
dbHandler.post(r);
}
}
The getHandler method returns null because the view is not attached:
public Handler getHandler() {
if (mAttachInfo != null) {
return mAttachInfo.mHandler;
}
return null;
}
mAttachInfo is set in dispatchAttachedToWindow and nulled in dispatchDetachedFromWindow.
Instead of mapView.getHandler().post() you can use directly mapView.post() (which seems to use getHandler().post() or ViewRootImpl.getRunQueue().post()).
I want to display a toast message in a static class but the is an issue of Toast message parameter passing context of application. Pleas help me, how to display the toast message in static class. Please recommend me the change that I have need to do, I will be very thankful to you. Here is a portion of my code.
public class MainActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.training_four_position);
mEndlessRunnable = (Runnable) new UpdateRunnable();
mEndlessRunnable.run();
}
private static class UpdateRunnable implements Runnable {
private int mState;
public UpdateRunnable(Handler handler, Button[] buttons) {
mHandler = handler;
mButtons = buttons;
}
public void run() {
switch (mState) {
case 0:
mState = 1;
break;
case 1:
mState = 0;
// Here is the issue in my toast message
Toast.makeText(CONTEXT, "Toast message.",Toast.LENGTH_LONG).show();
break;
}
mHandler.postDelayed(this,1000));
}// End of run()
}//End of class UpdateRunnable
} //End of MainActivity
you can try to make a separate method for your toast
public void showToast(String message){
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}//end showToast
and then call that in your inner class.
You can pass your Activity's Context to your UpdateRunnable class in constructor and use it then in your run() function.
However if you are showing Toast from an inner class then that inner class, probably, shouldn't be a static class at all. You can remove static keyword and use your Activity's getContext() in run() then.
Why do you want your inner class static?
Well, one of the way is to use a static variable in your activity.
public static Context myContext;
then update it in onCreate..
onCreate()
{
myContext = getApplicationContext();
}
Other way is to pass the context in the constructor of your class...
I guess getParent() or getApplicationContext() should do the work pass the parameter to the class and have a local context object .Let me know if it fails