I am using this code:
public class newtimer extends Activity {
private Timer myTimer;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
myTimer = new Timer();
myTimer.schedule(new TimerTask() {
#Override
public void run() {
TimerMethod();
}
}, 0, 1000);
}
int number = 0;
private void TimerMethod()
{
//This method is called directly by the timer
//and runs in the same thread as the timer.
//We call the method that will work with the UI
//through the runOnUiThread method.
Toast.makeText(this, "TimerMethod Running "+number, Toast.LENGTH_SHORT).show();
this.runOnUiThread(Timer_Tick);
}
private Runnable Timer_Tick = new Runnable() {
public void run() {
//This method runs in the same thread as the UI.
//Do something to the UI thread here
Toast.makeText(getBaseContext(), "UI Thread Running "+number, Toast.LENGTH_SHORT).show();
}
};
}
When i run it i get this exception in logcat:
09-06 21:39:39.701: ERROR/AndroidRuntime(1433): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
I would assume the problem is with the Toast.makeText(this, "TimerMethod Running "+number, Toast.LENGTH_SHORT).show(); in your TimerMethod function - you can't call any functions pertaining to the UI from worker threads. Since you already have a Toast in the portion that runs in the UI thread, why do you have another one in TimerMethod?
For debugging, I would recommend using Log as much as possible instead of Toast.
Related
I'm trying to understand how Handler works in a pair with Looper, but i have some problem. I need to do some long operation in a back thread and then to send some result in a textView.
I get the following error after pressing a button:
Only the original thread that created a view hierarchy can touch its views.
public class MainActivity extends AppCompatActivity {
Button mButton;
TextView mTextView;
ConsumeThread mConsumeThread;
class ConsumeThread extends Thread{
public Handler mHandler;
#Override
public void run() {
Looper.prepare();
mHandler = new Handler(){
#Override
public void handleMessage(Message msg){
int arg = msg.what;
someLongOperation(arg);
}
};
Looper.loop();
}
private void someLongOperation(int arg){
// do some long operation
arg += 1000;
mTextView.setText("Operation's code is " +arg); // fatal exception
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.txt_view);
mButton = (Button) findViewById(R.id.button);
mConsumeThread = new ConsumeThread();
mConsumeThread.start();
mButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
if(mConsumeThread.mHandler != null){
Message msg = mConsumeThread.mHandler.obtainMessage(10);
mConsumeThread.mHandler.sendMessage(msg);
}
}
});
}
To get Main Thread Handler You have get Handler as follows .
Because:-
Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it
So you need to get Handler which is associated with MainThread. For that you can use one of the following:-
With Context
Handler mainHandler = new Handler(getMainLooper()){
#Override
public void handleMessage(Message msg) {
}
};
Or Directly with Looper even when do not have Context
Handler mainHandler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
}
};
I assume that you are doing some long running task . So its better if you go with AsyncTask.
private void someLongOperation(int arg){
// do some long operation
arg =+ 1000;
mTextView.setText("Operation's code is " +arg); // fatal exception
}
//see here, you are in worker thread, so you can't excess UI toolkit or else exception, so if you want to do something ui related task in worker thread, use runOnUi, see here
runOnUiThread(new Runnable() {
#Override
public void run() {
textview.setText("");
}
})
You can't update the UI from another thread. You have to move the code that updates the UI to the UIThread.
Try Using:
runOnUiThread(new Runnable() {
#Override
public void run() {
//TextView Update Code
}
});
Tip: Try to reduce the number of lines of code you put inside this, as then there would be no purpose of using another thread.
Only the original thread that created a view hierarchy can touch its views
You have to do ui related work on the main thread...
So you can do it like this...
private void someLongOperation(int arg){
// do some long operation
arg =+ 1000;
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
mTextView.setText("Operation's code is " +arg); // fatal exception
}
});
}
I have a service. And there is a method called onServiceUpdate(). This method is similiar with onLocationChanged() in Google Maps API.
So i want to start CountDownTimer inside onServiceUpdate() method but showing error like this :
Can't create handler inside thread that has not called Looper.prepare()
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:200)
at android.os.Handler.<init>(Handler.java:114)
at android.os.CountDownTimer$1.<init>(CountDownTimer.java:114)
at android.os.CountDownTimer.<init>(CountDownTimer.java:114)
at skripsi.ubm.studenttracking.Service2$6.<init>(Service2.java:317)
at skripsi.ubm.studenttracking.Service2.onServiceUpdate(Service2.java:317)
This is my code :
#Override
public void onServiceUpdate(ServiceState state)
{
final float[] distance = new float[2];
Location.distanceBetween(state.getGeoPoint().getLatitude(), state.getGeoPoint().getLongitude(), 6.130607787619352,106.81839518499267, distance);
if (distance[0] > 25.0)
{
CountDownTimer cdt5 = new CountDownTimer(total_onServiceUpdate,1000) {
#Override
public void onTick(long millisUntilFinished) {
total_onServiceUpdate = millisUntilFinished/1000;
}
#Override
public void onFinish() {
sendSMS();
stopSelf();
}
}.start();
}
the onServiceUpdate() is an aysnchronous task that runs and notifies you, hence its a background thread. all you need to do is call timer.start(); from the main Thread, the Service actually runs on the main Thread, it is intentService that doesn't so, your solution is along the ways of
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
CountDownTimer cdt5 = new CountDownTimer(total_onServiceUpdate,1000) {
#Override
public void onTick(long millisUntilFinished) {
total_onServiceUpdate = millisUntilFinished/1000;
}
#Override
public void onFinish() {
sendSMS();
stopSelf();
}
}.start();
}
});
now you can continue sir. Always call codes the flirt with the Screen on the main Looper
Hope it helps
Android :
Try This before calling a Handler
if (Looper.MyLooper() == null) { Looper.Prepare(); }
I think the problem is that in your sendSMS() you are trying to do something which requires UIThread (like updating a view).
Try this:
Handler mHandler = new Handler() {
public void handleMessage(Message msg){
sendSMS();
}};
Modify the onFinish method to
#Override
public void onFinish() {
mHandler.sendEmptyMessage(0);
stopSelf();
}
You cannot update a activity gui from a service via a handler because the handler has to be created in the gui thread.
instead you have to send a broadcast from your service and implement a local broadcast-receiver in the activity that gets this broadcast
private void startUpdateTimerTask() {
TimerTask task = new TimerTask() {
#Override
public void run() {
doUpdate();
}
};
Timer timer = new Timer(true);
timer.schedule(task, ONE_MINUTE_MILLIS, ONE_HOUR_MILLIS);
}
private void doUpdate() {
new AsyncTask<Void,Void,Void>() {
#Override
protected Void doInBackground(Void... params) {
//....Network time-consuming tasks
return null;
}
}.equals();
}
(1)my question: When I run this function, there will be RuntimeException(No Looper; Looper.prepare() wasn't called on this thread.);
So I changed:
private void startUpdateTimerTask() {
TimerTask task = new TimerTask() {
#Override
public void run() {
Looper.prepare();
doUpdate();
Looper.loop()
}
};
Timer timer = new Timer(true);
timer.schedule(task, ONE_MINUTE_MILLIS, ONE_HOUR_MILLIS);
}
then RuntimeException does not appear ,but doUpdate() Executed only once?
(2) Question: How to achieve access to the network to update information every 1 hour?
then RuntimeException does not appear, but doUpdate() Executed only
once?
This is because an asynctask can execute only once.The doInBackground() runs on a separate thread, and once a thread has completed its process, you cannot start it again. Since you are already using timer task, the timer task performs operation on separate worker thread, so you can perform the same operation in the run() of timer task, which you are performing in doInBackground() of AsyncTask. For updating your UI, you can make use of Runnable.
I'm writing an Android application and I need a Timer to be set which will execute a method every one second and then stop once a boolean variable (set by aforementioned method) becomes true.
Here's an overview of what I'm trying to do:
boolean done = false;
public void someMethod() {
if(done == false) {
myTimer = new Timer(); //Set up a timer, to execute TimerMethod repeatedly
myTimer.schedule(new TimerTask() {
#Override
public void run() {
TimerMethod();
}
}, 0, 1000);
}
if(done == true) {
//TimerMethod will eventually set 'done' to true. When this happen, do layout modifying stuff here. Causes error as non-UI thread is executing the layout modifying stuff. Do I spawn a new UI thread here to do it? If so, how? :/
}
}
TimerMethod() {
String result = someServerMethod();
if(result == "theResultWeWant") {
myTimer.cancel(); //stop the timer - we're done!
done = true; //set 'done' to true so the line of code in someMethod() will now run
someMethod();
}
}
Edit: I've updated the code above to reflect what I'd like to do. I'm hoping I can get the done flash to be set to true and then carry on executing someMethod but I'm sure it's not that simple! Do I perhaps need to spawn a new thread from TimerMethod() to execute the code from the done == true line?
All UI interactions need to be done from the main(UI) thread. In your case, you were calling someMethod() from your TimerTask which is a seperate thread. A handler is used to interact with your main thread from a helper thread.
public void someMethod() {
myTimer = new Timer(); //Set up a timer, to execute TimerMethod repeatedly
myTimer.schedule(new TimerTask() {
#Override
public void run() {
TimerMethod();
}
}, 0, 1000);
}
}
TimerMethod() {
String result = someServerMethod();
if(result.equals("theResultWeWant")) {
myTimer.cancel(); //stop the timer - we're done!
mHandler.sendEmptyMessage(0): //send message to handler to update UI
}
}
private Handler mHandler = new Handler(){
public void handleMessage(Message msg) {
doUIMethod();
}
};
I have an Activity, and in that I have a class.
text=new Dynamictext(...);
text.setText("txt");
in my DynamicText java I have this code:
public void setText(String text) {
this.text=text;
new asyncCreateText().execute();
//this.createText(text);
}
//private Handler handler = new Handler();
private class asyncCreateText extends AsyncTask<Void, Void, Void>
{
#Override
protected Void doInBackground(Void... unused) {
return null;
}
#Override
protected void onPostExecute(Void unused) {
}
}
I get:
ERROR/AndroidRuntime(5176): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
How can I handle this error?
ERROR/AndroidRuntime(5370): java.lang.ExceptionInInitializerError
ERROR/AndroidRuntime(5370): at com.l.start.DynamicText.setText(DynamicText.java:125)
ERROR/AndroidRuntime(5370): at com.l.start.OpenGLRenderer.initfonts(OpenGLRenderer.java:168)
ERROR/AndroidRuntime(5370): at com.l.start.OpenGLRenderer.init(OpenGLRenderer.java:119)
ERROR/AndroidRuntime(5370): at com.l.start.OpenGLRenderer.onSurfaceChanged(OpenGLRenderer.java:90)
ERROR/AndroidRuntime(5370): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1120)
ERROR/AndroidRuntime(5370): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:975)
ERROR/AndroidRuntime(5370): Caused by: java.lang.RuntimeException:
Can't create handler inside thread that has not called Looper.prepare()
ERROR/AndroidRuntime(5370): at android.os.Handler.<init>(Handler.java:121)
ERROR/AndroidRuntime(5370): at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
ERROR/AndroidRuntime(5370): at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
ERROR/AndroidRuntime(5370): at android.os.AsyncTask.<clinit>(AsyncTask.java:152)
ERROR/AndroidRuntime(5370): ... 6 more
The error is self-explanatory... doInBackground() runs on a background thread which, since it is not intended to loop, is not connected to a Looper.
You most likely don't want to directly instantiate a Handler at all... whatever data your doInBackground() implementation returns will be passed to onPostExecute() which runs on the UI thread.
mActivity = ThisActivity.this;
mActivity.runOnUiThread(new Runnable() {
public void run() {
new asyncCreateText().execute();
}
});
ADDED FOLLOWING THE STACKTRACE APPEARING IN QUESTION:
Looks like you're trying to start an AsyncTask from a GL rendering thread... don't do that cos they won't ever Looper.loop() either. AsyncTasks are really designed to be run from the UI thread only.
The least disruptive fix would probably be to call Activity.runOnUiThread() with a Runnable that kicks off your AsyncTask.
All the answers above are correct, but I think this is the easiest example possible:
public class ExampleActivity extends Activity {
private Handler handler;
private ProgressBar progress;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
progress = (ProgressBar) findViewById(R.id.progressBar1);
handler = new Handler();
}
public void clickAButton(View view) {
// Do something that takes a while
Runnable runnable = new Runnable() {
#Override
public void run() {
handler.post(new Runnable() { // This thread runs in the UI
#Override
public void run() {
progress.setProgress("anything"); // Update the UI
}
});
}
};
new Thread(runnable).start();
}
}
What this does is update a progress bar in the UI thread from a completely different thread passed through the post() method of the handler declared in the activity.
Hope it helps!
You create handler in background thread this way
private void createHandler() {
Thread thread = new Thread() {
public void run() {
Looper.prepare();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Do Work
handler.removeCallbacks(this);
Looper.myLooper().quit();
}
}, 2000);
Looper.loop();
}
};
thread.start();
}
Activity.runOnUiThread() does not work for me. I worked around this issue by creating a regular thread this way:
public class PullTasksThread extends Thread {
public void run () {
Log.d(Prefs.TAG, "Thread run...");
}
}
and calling it from the GL update this way:
new PullTasksThread().start();
Try running you asyntask from the UI thread. I faced this issue when I wasn't doing the same!
Try this
Handler mHandler = new Handler(Looper.getMainLooper());
mHandler.post(new Runnable() {
#Override
public void run() {
//your code here that talks with the UI level widgets/ try to access the UI
//elements from this block because this piece of snippet will run in the UI/MainThread.
}
});
For all the Kotlin lovers, here is the code for you
Handler(Looper.getMainLooper()).post {
// any UI update here
}