Can anyone explain when I put the thread to sleep for 30 milliseconds it updates the UI from the background thread. but when I put the sleep for 300 milliseconds it crashes saying that you cannot touch view from another thread. I expect it to crash in both cases but somehow it is working in 30 milliseconds. Needs some clarification on this issue.
public class HandlerDemo extends Activity implements Handler.Callback{
private static final String TAG = "FFFF";
private Handler mHandler = null;
private Handler mUIHandler = null;
private HandlerThread backgroundThread = null;
public static final int BACKGROUND_OPERATION = 10;
public static final int MAIN_THREAD_OPERATION = 20;
private TextView asd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HandlerThread backgroundThread = new HandlerThread("BACKGROUND_THREAD");
backgroundThread.start();
mHandler = new Handler(backgroundThread.getLooper(), this);
mHandler.sendEmptyMessage(BACKGROUND_OPERATION);
// mUIHandler = new Handler(Looper.getMainLooper(), this);
// mUIHandler.sendEmptyMessage(MAIN_THREAD_OPERATION);
}
#Override
protected void onDestroy() {
super.onDestroy();
mHandler.getLooper().quit();
}
#Override
public boolean handleMessage(Message message) {
Log.d(TAG, Thread.currentThread() + "");
switch (message.what) {
case MAIN_THREAD_OPERATION:
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
((TextView) findViewById(R.id.textview)).setText("Updating from UI Handler");
findViewById(R.id.textview).invalidate();
break;
case BACKGROUND_OPERATION:
try {
**Thread.sleep(300);**
} catch (InterruptedException e) {
e.printStackTrace();
}
((TextView) findViewById(R.id.textview)).setText("Updating from background Handler");
findViewById(R.id.textview).invalidate();
break;
default:
break;
}
// message.recycle();
return true;
}
}
runOnUiThread(new Runnable() {
#Override
public void run() {
// do what you want
}
});
Related
I'm new to android and I want to do a simple example where I click on a start button and another Activity is open, there a simple number starting in one and counting upwards, but I'm facing a problem, after I initialize some variables on onCreate method (In the second activity), where should I actually start the while statement to count and modify the text view?.
I wrote this class:
public class Counter extends Thread{
private TextView tv;
private int i;
public Counter( TextView tv ){
this.tv = tv;
}
#Override
public void run() {
while( true ){
tv.setText(i);
try {
sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
i++;
}
}
And started the thread over here:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
counter = new Counter( (TextView) findViewById(R.id.textView2) );
counter.start( );
}
#SuppressLint("UseValueOf")
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
// change your text here
if (condition) {
i++;
txt_TimeRecord.setText("" + i);
} else {
i = 0;
}
handler.postDelayed(this, 1 * 1000L);
}
});
[Formatted the code properly]
//In first activity
//set onclick method to that button.
public void onclick(view v)
{
Intent intent = new Intent(this, secondactivity.class);
startActivity(intent);
}
// in second activity in
// initi i value.
while(i<10)
{
i++ ;
// sleep statement
//print that i in textview
}
I've found what I need from a similar post, the handler was needed since only the UI Thread can update the user interface.
private void countNumbers() {
final TextView numbers = (TextView) findViewById(R.id.textView2);
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
public void run() {
int i = 0;
while (i++ < 500) {
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
e.printStackTrace();
}
final int j = i;
handler.post(new Runnable(){
public void run() {
numbers.setText(Integer.toString(j));
}
});
}
}
};
new Thread(runnable).start();
}
The method countNumbers() is called in onCreate().
I cant figure out why i cant set text to my textView tv.
getting:
E/AndroidRuntime(686): android.view.ViewRoot$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
I tried many ways to make it right.
As you can see i tried Handler because i had the same problem with toasts. Now toast works but setText doesnt :((
Please someone help me, how should i configure this handler?
public class calculate extends Activity implements OnClickListener {
private myService myService; //bound service instance
private boolean serviceStarted;
View show_map;
View data;
View start;
View stop;
public TextView tv;
private Location loc;
private boolean initiated=false;
private float distance=0;
UIHandler uiHandler;
route_calc rc;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.calculate);
tv=(TextView)findViewById(R.id.textView1);
show_map=findViewById(R.id.button1);
show_map.setOnClickListener(this);
data=findViewById(R.id.button2);
data.setOnClickListener(this);
start=findViewById(R.id.button3);
start.setOnClickListener(this);
stop=findViewById(R.id.button4);
stop.setVisibility(View.INVISIBLE);
stop.setOnClickListener(this);
HandlerThread uiThread = new HandlerThread("UIHandler");
uiThread.start();
uiHandler = new UIHandler( uiThread.getLooper());
}
public void onDestroy(){
super.onDestroy();
}
#Override
public void onClick(View v) {
Intent i;
switch(v.getId()){
case R.id.button1:
i=new Intent(this,Map.class);
startActivity(i);
break;
case R.id.button2:
i=new Intent(this,data.class);
startActivity(i);
break;
case R.id.button3:
startService();
break;
case R.id.button4:
stopService();
break;
}
}
//connection between this activity and service myService
ServiceConnection myServConn = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName arg0) {
myService = null;
}
#Override
public void onServiceConnected(ComponentName arg0, IBinder binder) {
myService = ((myService.MyBinder)binder).getMyService();
}
};
private void startService() {
Intent intent = new Intent(this, myService.class);
startService(intent);
//Bind MyService here
bindService(intent, myServConn, BIND_AUTO_CREATE);
stop.setVisibility(View.VISIBLE);
serviceStarted = true;
rc = new route_calc();
rc.start();
}
private void stopService() {
if(serviceStarted) {
Intent intent = new Intent(this, myService.class);
//Unbind MyService here
unbindService(myServConn);
stopService(intent);
stop.setVisibility(View.INVISIBLE);
serviceStarted = false;
}
}
void showToast(String s){
handleUIRequest(s);
}
void setText(){
handleUISetText();
}
class route_calc extends Thread{
Location begin;
public void run() {
float temp;
while(!initiated){
try{
loc=myService.getLocation();
}
catch(Exception e){
}
if(loc!=null){
begin=loc;
initiated=true;
showToast("zadzialalo");
}
}
while(true){
loc=myService.getLocation();
temp=begin.distanceTo(loc);
distance=distance+temp;
tv.setText("przejechales "+distance+" m");
System.err.println(distance);
begin=loc;
try {
this.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private final class UIHandler extends Handler
{
public static final int DISPLAY_UI_TOAST = 0;
public static final int TV_SET_TEXT = 1;
public UIHandler(Looper looper)
{
super(looper);
}
public void handleMessage(Message msg)
{
switch(msg.what)
{
case UIHandler.DISPLAY_UI_TOAST:
{
Context context = getApplicationContext();
Toast t = Toast.makeText(context, (String)msg.obj, Toast.LENGTH_LONG);
t.show();
}
case UIHandler.TV_SET_TEXT:
{
tv.setText("przejechałeś "+distance+" m");
}
default:
break;
}
}
}
protected void handleUIRequest(String message)
{
Message msg = uiHandler.obtainMessage(UIHandler.DISPLAY_UI_TOAST);
msg.obj = message;
uiHandler.sendMessage(msg);
}
protected void handleUISetText(){
Message msg=uiHandler.obtainMessage(UIHandler.TV_SET_TEXT);
uiHandler.sendMessage(msg);
}
}
It seems like you put your entire Activity here, and that it also includes a service, and you didn't try to narrow down your problem.
in your route_calc thread you call showToast, this is probably one of your problems, you should call showToast (or any other UI function) from your Handler.
Something like this:
Do anything you want on your thread:
new Thread(new Runnable()
{
#Override
public void run()
{
try
{
someHeavyStuffHere(); //Big calculations or file download here.
handler.sendEmptyMessage(SUCCESS);
}
catch (Exception e)
{
handler.sendEmptyMessage(FAILURE);
}
}
}).start();
When your data is ready, tell the handler to put it in a view and show it:
protected Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
if (msg.what == SUCCESS)
{
setCalculatedDataToaView(); // the data you calculated from your thread can now be shown in one of your views.
}
else if (msg.what == FAILURE)
{
errorHandlerHere();//could be your toasts or any other error handling...
}
}
};
I have a splashscreen which lasts 5 seconds, and I want to represent the progress using a ProgressBar.
public class SplashActivity extends Activity {
private static final long SPLASHTIME = 5000;
private ProgressBar progressBar;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.splash);
SplashHandler handlerSplash = new SplashHandler();
progressBar = (ProgressBar) findViewById(R.id.progression);
progressBar.setMax((int) ((SPLASHTIME) / 1000));
progressBar.setProgress(0);
Message msg = new Message();
msg.what = 0;
handlerSplash.sendMessageDelayed(msg, SPLASHTIME);
ThreadProgressBar threadProgress = new ThreadProgressBar();
threadProgress.start();
}
private class ThreadProgressBar extends Thread {
ProgressBarHandler handlerProgress = new ProgressBarHandler();
public void run() {
try {
while (progressBar.getProgress() <= progressBar.getMax()) {
Thread.sleep(1000);
handlerProgress.sendMessage(handlerProgress.obtainMessage());
}
} catch (java.lang.InterruptedException e) {
}
}
}
private class ProgressBarHandler extends Handler {
public void handleMessage(Message msg) {
progressBar
.incrementProgressBy((int) (SPLASHTIME / SPLASHTIME));
}
}
private class SplashHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
default:
case 0:
super.handleMessage(msg);
// new ProgressBarIncrease().execute();
Intent intent = new Intent();
intent.setClass(SplashActivity.this, RdvTab.class);
startActivity(intent);
}
}
}
I guess since SplashActivity itself is an activity, it is ok to block UI right? AsyncTask sometimes run not gradually, if you want the update to be displayed smoothly, how about changing to worker Thread and Handlder.
Create Handler inside SplashActivity
In the main Thread, display progressBar or splash or whatever you like
While make another Thread to run the countdown, once in every second sendEmptyMessage() to Handler. After 5 seconds, send a message to Handler to dismiss dialog and splash then the worker Thread ends.
UPDATED: Sorry for the slow feedback. How about this? I haven't tested it yet and it's not the best implementation though, you can use this logic
public class SplashActivity extends Activity {
private static final int SPLASHTIME = 5000;
// u can change the value here for smoother effect
private static final long UPDATEINTERVAL = 1000;
private ProgressBar progressBar;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.splash);
progressBar = (ProgressBar) findViewById(R.id.progression);
new Thread(new Runnable(){
#Override
public void run(){
int progress = 0;
while(true){
// send current progress to handler
mHandler.sendEmptyMessage(progress);
// break from the loop after SPLASHSCREEN millis
if(progress > SPLASHSCREEN)
break;
// increase the progress
progress = (int) (progress + UPDATEINTERVAL);
// sleep the worker thread
Thread.sleep(UPDATEINTERVAL);
}
}
}).start();
}
private Handler mHandler = new Handler(){
#Override
public void handleMessage(Message message){
// get the current progress
int progress = message.what;
if(progress <= SPLASHTIME){
progressBar.setProgress(progress);
}
else{
// finish splashActivity here & do what u want do to after splashscreen, for example
finish();
Intent intent = new Intent(SpashActivity.this, MenuActivity.class);
startActivity(intent);
}
}
}
}
You should divide the splash screen time into equal amounts and send the progress update at those intervals.
For example:Lets say the total time is divided into 50 equal intervals as follows
protected void onPreExecute() {
progressbarStep = progressbarWidth/50;
timeStep = 5000/50;
progress = 0;
}
#Override
protected Void doInBackground(Void... params) {
try {
while(progress < progressbarWidth){
progress += progressbarStep;
publishProgress(progress);
SystemClock.sleep(timeStep);
}
return null;
} catch (Exception e) {
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
progressBar.setProgress(values[0]);
}
I get this error "Can't create handler inside thread that has not called Looper.prepare()"
Can you tell me how to fix it?
public class PaymentActivity extends BaseActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.payment);
final Button buttonBank = (Button) findViewById(R.id.buttonBank);
buttonBank.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
progressDialog = ProgressDialog.show(PaymentActivity.this, "",
"Redirecting to payment gateway...", true, true);
new Thread() {
public void run() {
try {
startPayment("Bank");
} catch (Exception e) {
alertDialog.setMessage(e.getMessage());
handler.sendEmptyMessage(1);
progressDialog.cancel();
}
}
}.start();
}
});
StartPayment Method:
private void startPayment(String id) {
Bundle b = getIntent().getExtras();
final Sail sail = b.getParcelable(Constant.SAIL);
final Intent bankIntent = new Intent(this, BankActivity.class);
try {
Reservation reservation = RestService.createReservation(
sail.getId(),
getSharedPreferences(Constant.PREF_NAME_CONTACT, 0));
bankIntent.putExtra(Constant.RESERVATION, reservation);
// <workingWithDB> Storing Reservation info in Database
DBAdapter db = new DBAdapter(this);
db.open();
#SuppressWarnings("unused")
long rowid;
rowid = db.insertRow(sail.getId(), sail.getFrom(),
sail.getTo(), sail.getShip(), sail.getDateFrom().getTime(),
sail.getPrice().toString(), reservation.getId().floatValue());
db.close();
// </workingWithDB>
String html = PaymentService.getRedirectHTML(id, reservation);
bankIntent.putExtra(Constant.BANK, html);
} catch (Exception e) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
AlertDialog alertDialog = builder.create();
alertDialog.setMessage(e.getMessage());
alertDialog.show();
}
startActivity(bankIntent);
}
You should know that when you try to modify your UI , the only thread who can do that is the UiThread.
So if you want to modify your UI in another thread, try to use the method: Activity.runOnUiThread(new Runnable);
Your code should be like this :
new Thread() {
public void run() {
YourActivity.this.runOnUiThread(new Runnable(){
#Override
public void run(){
try {
startPayment("Bank");//Edit,integrate this on the runOnUiThread
} catch (Exception e) {
alertDialog.setMessage(e.getMessage());
handler.sendEmptyMessage(1);
progressDialog.cancel();
}
});
}
}
}.start();
I assume you create a Handler in startPayment() method. You can't do that, as handlers can be created on th UI thread only. Just create it in your activity class.
Instead of new Thread() line, try giving
this.runOnUiThread(new Runnable() {
you cant change any UI in thread you can use runOnUIThread or AsyncTask for more detail about this click here
I've found that most thread handling can be replaced by AsyncTasks like this:
public class TestStuff extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button buttonBank = (Button) findViewById(R.id.button);
buttonBank.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new StartPaymentAsyncTask(TestStuff.this).execute((Void []) null);
}
});
}
private class StartPaymentAsyncTask extends AsyncTask<Void, Void, String> {
private ProgressDialog dialog;
private final Context context;
public StartPaymentAsyncTask(Context context) {
this.context = context;
}
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(context);
// setup your dialog here
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setMessage(context.getString(R.string.doing_db_work));
dialog.setCancelable(false);
dialog.show();
}
#Override
protected String doInBackground(Void... ignored) {
String returnMessage = null;
try {
startPayment("Bank");
} catch (Exception e) {
returnMessage = e.getMessage();
}
return returnMessage;
}
#Override
protected void onPostExecute(String message) {
dialog.dismiss();
if (message != null) {
// process the error (show alert etc)
Log.e("StartPaymentAsyncTask", String.format("I received an error: %s", message));
} else {
Log.i("StartPaymentAsyncTask", "No problems");
}
}
}
public void startPayment(String string) throws Exception {
SystemClock.sleep(2000); // pause for 2 seconds for dialog
Log.i("PaymentStuff", "I am pretending to do some work");
throw new Exception("Oh dear, database error");
}
}
I pass in the Application Context to the Async so it can create dialogs from it.
The advantage of doing it this way is you know exactly which methods are run in your UI and which are in a separate background thread. Your main UI thread isn't delayed, and the separation into small async tasks is quite nice.
The code assumes your startPayment() method does nothing with the UI, and if it does, move it into the onPostExecute of the AsyncTask so it's done in the UI thread.
Try
final Handler handlerTimer = new Handler(Looper.getMainLooper());
handlerTimer.postDelayed(new Runnable() {
public void run() {
......
}
}, time_interval});
I am trying to update a textview after 3 seconds, and i want to do that in a loop for which i am using thread .....what should i do in order to start background thread so that exactly after 3 seconds background1 thread starts ... and it should be done in a loop ?
package com.edu.math;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;
public class EduMath extends Activity {
TextView txt;
TextView tv;
TextView num;
String x;
int time = 3000;
float z ;
int random;
int random1;
int random_operator =1;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
txt=(TextView)findViewById(R.id.txt);
tv = (TextView)findViewById(R.id.randomNumber);
background.start();
}
// our handler
Handler handler = new Handler() {
public void handleMessage(Message msg) {//display each item in a single line
random = (int)Math.ceil(Math.random()*10);
random1 = (int)Math.ceil(Math.random()*10);
if(random1>random)
{
txt.setText(/*txt.getText()+*/""+random1+" "+>+" "+random+" "+
System.getProperty("line.separator"));
}
else
{
txt.setText(/*txt.getText()+*/""+random+" "+>+" "+random1+
System.getProperty("line.separator"));
}
}
};
Handler handler1 = new Handler() {
#Override
public void handleMessage(Message msg) {//display each item in a single line
txt.setText(/*txt.getText()+*/""+random1+" "+x+" "+random+System.getProperty("line.separator"));
}
};
Thread background1 =new Thread(new Runnable() {
#Override
public void run() {
for(int i=0;i<10;i++)
{
try {
Thread.sleep(time);
// send message to the handler with the current message handler
handler1.sendMessage(handler1.obtainMessage());
background.stop();
Log.d("EduMath-enclosing_method", "thread started");
} catch (Exception e) {
Log.v("Error", e.toString());
}
}
}
});
Thread background=new Thread(new Runnable() {
#Override
public void run() {
for(int i=0;i<10;i++)
{
try {
Thread.sleep(time);
// send message to the handler with the current message handler
handler.sendMessage(handler.obtainMessage());
background1.start();
} catch (Exception e) {
Log.v("Error", e.toString());
}
}
}
});
}
You must use AsyncTask for changing any views in layout, but at now I have very little experience with AsyncTask.
Try to use a java.util.Timer ( http://download.oracle.com/javase/1.4.2/docs/api/java/util/Timer.html )
public class HandlerDemo extends Activity {
ProgressBar bar;
Handler handler=new Handler() {
#Override
public void handleMessage(Message msg) {
//add your text
}
};
AtomicBoolean isRunning=new AtomicBoolean(false);
public void onStart() {
super.onStart();
Thread background=new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(3000);
handler.sendMessage(handler.obtainMessage());
}
catch (Throwable t) {
// just end the background thread
}
}
});
isRunning.set(true);
background.start();
}
public void onStop() {
super.onStop();
isRunning.set(false);
}
}
Try the solution on this post...seems to be what you looking for http://androidgenuine.com/?p=707