I'm creating a button to stop a light sensor after grabbing two equal light values for a duration of 2 seconds using the handler's delay function. It works fine at first but when I press the button again, the delay seems to get shorter and shorter, and eventually doesn't happen at all.
stateLx.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
onResume();
stateLx.setEnabled(false);
doub2x.setText(Double.toString(0));
doub1x.setText(Double.toString(0));
//Stabilization Handlers
m_handler = new Handler();
m_handlerTask = new Runnable() {
public void run() {
doub1 = movingValue[0];
doub1x.setText(Double.toString(doub1));
m_handler.postDelayed(this, 1000);
}
};
m2_handler = new Handler();
m2_handlerTask = new Runnable() {
public void run() {
doub2 = movingValue[0];
doub2x.setText(Double.toString(doub2));
m2_handler.postDelayed(this, 2000);
if (doub1 != doub2) {
instructions.setText("unequal");
} else {
instructions.setText("equal");
getInitialLightValue();
reCalculateInitial();
beerLambert(volValues, lxValuesNew);
}
}
};
//Handlers for stabilizer
m_handler.postDelayed(m_handlerTask, 1000);
m2_handler.postDelayed(m2_handlerTask, 3000);
lame.setText(Double.toString(absorbValues[0]));
stateLx.setEnabled(true);
}
});
I've tried putting the handlers both in and out of the onclick function, as well as including the onResume() function at the beginning of the button press, but I'm having no luck. Is there something wrong with my code?
You keep recreating your Handlers and Runnables. This is part of the problem, you should check to see if you are already polling before starting again.
Also, this is a side note -- but it's odd to call lifecycle methods, like onResume() yourself, since it calls up to the super.
Related
I want to solve a problem that I have been trying to do so the last couple of days but I dont have that much experience and I couldnt find the solution enywhere else.
Anyway,
In my app I have a button in wich I have implemented the onClickClistener in order to respond to touch and inside that I have added a Handler which adds a delay and after that some code is being executed. My problem is that i want to detect any tap of the button whilst the delay is happening and the postDelyed function doesn't allow me to do so. How can I actually do that?
I've posted my code that is related on that.
Thanks in advance!
P.S(I dont mind not using this postDelayed thing.)
Button button = findViewById(R.id.myButtonId);
button.setOnClickListener(new View.OnClickListener){
#Override
public void onClick(View v) {
.......
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do some thing after the delay
}
}, randomDelay);
//Do other things
}
});
boolean previousTapDetected;
...
button.setOnClickListener(new View.OnClickListener){
#Override
public void onClick(View v) {
.......
if(previousTapDetected) {
//We got a tap during the delay
}
Handler handler = new Handler();
previousTapDetected = true;
handler.postDelayed(new Runnable() {
#Override
public void run() {
previousTapDetected = false;
//Do some thing after the delay
}
}, randomDelay);
//Do other things
}
});
So I have an android app where I want to decrement a value and display it in a textview. I start from 1000 and decrement it by 1 from 1 to 1 seconds. This acts as a score that decreases in time if you stay more on the level. This is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game);
timeText = (TextView) findViewById(R.id.textView5);
runOnUiThread(new Runnable() {
#Override
public void run() {
for(time=1000;time>=0;time--){
try {
TimeUnit.SECONDS.sleep(1);
timeText.setText(String.valueOf(time));
System.out.println(time);
}
catch(Exception e)
{
System.out.println("Error on delay");
}
}}
});
}
My error is that whenever I enter this activity, the screen turns black. The console is printing the values from second to second and if i comment the "for" the textView displays properly the value 1000 (if i declare int time = 1000 of course). I am really not sure what the problem is here. Does somebody know what i'm doing wrong?
You can't just loop on the UI thread like that. Inside Android there's a message loop on the UI thread. When it needs to draw, it sends a message to that message loop. Until you process that message the changes won't appear on screen. And to process a message, your code must finish and return to the message loop.
If you want to do this, you can't use a for loop on the UI thread. You need to send individual messages to a Handler for each draw you want to make.
You are pausing the UI in that for loop.
To achieve what you want, either use a Handler
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//update textview here
}
},1000);
OR
use a Timer
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
//update
}
},1000,0);
I don't know much about handlers but you don't need a for loop in timer.
In your case:
long time=1000;
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable(){
timeText.setText(String.valueOf(time))
time--;
});
}
},1000,0).start();
Good luck
Ok - I know there has got to be a simple solution to this but for the life of me I can't figure it out.
Programming a very basic android activity to simply iterate through 0-99. I have a textview that I want to display the count. What happens is that it simply stays blank until the end and then shows the ending count (99).
Not sure if a textview is the right way to display or what the answer is. Any help is greatly appreciated. Thanks in advance
Try using code like this in onCreate (where number is defined as a field):
textView.post(new Runnable() {
#Override
public void run() {
number++;
textView.setText("counting: " + number);
if (number < 100) {
textView.postDelayed(this, 50);
}
}
});
Edit: code was edited as View classes have post and postDelayed, which propagates call to Handler instance they have internally.
You need to read a bit about Handler class.
Warning: this code leaks Activity for the time of approximatelly 5 seconds and should not be used directly in production code. You need to remove Runnable from the message queue at the appropriate time (maybe in onDestroy, but it depends on your needs).
View.removeCallbacks for anti-memory-leak.
My guess is that your onCreate() has code like this:
for (int i=0;i<100;i++) {
tv.setText(String.valueOf(i));
Thread.sleep(100); // or something to delay for a bit
}
That will give you the output that you are describing.
As with many GUI frameworks, Android's UI is event-driven. Calling setText() does not update the screen. Rather, it puts a message on a queue, asking for the screen to be updated. That queue is processed by the main application thread... the same thread that is calling onCreate() in the first place. Hence, what you are doing is queuing up 100 setText() calls, none of which will be processed until your loop is complete. Applying the 100 of them takes very little time, giving the visual result of only seeing the last change.
User a timer scheduled at a fixed rate. Increment a counter every second. Set the text on the UI thread. cancel the timer when required.
public class MainActivity extends Activity {
TextView _tv;
Timer _t;
int _count=0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_tv = (TextView) findViewById( R.id.textView1 );
_t = new Timer();
_tv.setText(""+_count);
_t.scheduleAtFixedRate( new TimerTask() {
#Override
public void run() {
_count++;
runOnUiThread(new Runnable() //run on ui thread
{
public void run()
{
_tv.setText(""+_count);
if(_count==99)
{
_t.cancel();
}
}
});
}
}, 1000, 1000 );
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
_t.cancel();
}
}
Use a countdown timer, in below code, onTick() will get called every second, here you can display/update your number each second.
set interval according to your need. Its in mili seconds.
public class TimerActivity extends Activity {
private final long startTime = 100 * 1000;
private final long interval = 1 * 1000;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
countDownTimer = new MyCountDownTimer(startTime, interval);
countDownTimer.start();
public class MyCountDownTimer extends CountDownTimer {
public MyCountDownTimer(long startTime, long interval) {
super(startTime, interval);
}
#Override
public void onFinish() {
text.setText("Time's up!");
}
#Override
public void onTick(long millisUntilFinished) {
text.setText(100 - millisUntilFinished/1000);
}
}
}
I have an AsyncTask class that I execute that downloads a big list of data from a website.
In the case that the end user has a very slow or spotty data connection at the time of use, I'd like to make the AsyncTask timeout after a period of time. My first approach to this is like so:
MyDownloader downloader = new MyDownloader();
downloader.execute();
Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
#Override
public void run() {
if ( downloader.getStatus() == AsyncTask.Status.RUNNING )
downloader.cancel(true);
}
}, 30000 );
After starting the AsyncTask, a new handler is started that will cancel the AsyncTask after 30 seconds if it's still running.
Is this a good approach? Or is there something built into AsyncTask that is better suited for this purpose?
Yes, there is AsyncTask.get()
myDownloader.get(30000, TimeUnit.MILLISECONDS);
Note that by calling this in main thread (AKA. UI thread) will block execution, You probably need call it in a separate thread.
Use CountDownTimer Class in side the extended class for AsyncTask in the onPreExecute() method:
Main advantage, the Async monitoring done internally in the class.
public class YouExtendedClass extends AsyncTask<String,Integer,String> {
...
public YouExtendedClass asyncObject; // as CountDownTimer has similar method -> to prevent shadowing
...
#Override
protected void onPreExecute() {
asyncObject = this;
new CountDownTimer(7000, 7000) {
public void onTick(long millisUntilFinished) {
// You can monitor the progress here as well by changing the onTick() time
}
public void onFinish() {
// stop async task if not in progress
if (asyncObject.getStatus() == AsyncTask.Status.RUNNING) {
asyncObject.cancel(false);
// Add any specific task you wish to do as your extended class variable works here as well.
}
}
}.start();
...
change CountDownTimer(7000, 7000) -> CountDownTimer(7000, 1000) for example and it will call onTick() 6 times before calling onFinish(). This is good if you want to add some monitoring.
Thanks for all the good advice I got in this page :-)
In the case, your downloader is based upon an for an URL connection, you have a number of parameters that could help you to define a timeout without complex code:
HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
urlc.setConnectTimeout(15000);
urlc.setReadTimeout(15000);
If you just bring this code into your async task, it is ok.
'Read Timeout' is to test a bad network all along the transfer.
'Connection Timeout' is only called at the beginning to test if the server is up or not.
I don't think there's anything like that built into AsyncTask. Your approach seems to be a good one. Just be sure to periodically check the value of isCancelled() in your AsyncTask's doInBackground method to end this method once the UI thread cancels it.
If you want to avoid using the handler for some reason, you could check System.currentTimeMillis periodically within your AsyncTask and exit on timeout, although I like your solution better since it can actually interrupt the thread.
Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
//async task
final RunTask tsk = new RunTask ();
tsk.execute();
//setting timeout thread for async task
Thread thread1 = new Thread(){
public void run(){
try {
tsk.get(30000, TimeUnit.MILLISECONDS); //set time in milisecond(in this timeout is 30 seconds
} catch (Exception e) {
tsk.cancel(true);
((Activity) mContext).runOnUiThread(new Runnable()
{
#SuppressLint("ShowToast")
public void run()
{
Toast.makeText(mContext, "Time Out.", Toast.LENGTH_LONG).show();
finish(); //will close the current activity comment if you don't want to close current activity.
}
});
}
}
};
thread1.start();
}
You can put one more condition to make cancellation more robust. e.g.,
if (downloader.getStatus() == AsyncTask.Status.RUNNING || downloader.getStatus() == AsyncTask.Status.PENDING)
downloader.cancel(true);
Inspiring from question I have written a method which do some background task via AsyncTask and if processing takes more then LOADING_TIMEOUT then an alert dialogue to retry will appear.
public void loadData()
{
final Load loadUserList=new Load();
loadUserList.execute();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (loadUserList.getStatus() == AsyncTask.Status.RUNNING) {
loadUserList.cancel(true);
pDialog.cancel();
new AlertDialog.Builder(UserList.this)
.setTitle("Error..!")
.setMessage("Sorry you dont have proper net connectivity..!\nCheck your internet settings or retry.")
.setCancelable(false)
.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
loadData();
}
})
.setNegativeButton("Exit", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
System.exit(0);
}
})
.show();
}
}
}, LOADING_TIMEOUT);
return;
}
So I have some simple code but it seems to not be working.. any suggestions?
I just want an image to show after a button is pressed then become invisible after 2 seconds.
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
firstImage.setVisibility(ImageView.VISIBLE);
// delay of some sort
firstImage.setVisibility(ImageView.INVISIBLE);
}
}
The image never shows, it always stays invisible, should I be implementing this in another way? I've tried handlers.. but it didn't work, unless I did it wrong.
Never make your UI thread sleep!
Do this:
final Handler handler = new Handler();
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
firstImage.setVisibility(ImageView.VISIBLE);
handler.postDelayed(new Runnable(){
public void run(){
firstImage.setVisibility(ImageView.INVISIBLE);
}
}, DELAY);
}
}
Where you would set DELAY as 2000 (ms).
Well, you will need to add a delay between the two lines. Use a thread or a timer to do this.
Start a thread on click of a button. In the run method, change the ImageView's visibility to VISIBLE, then put the thread to sleep for n secs, and then change then make it invisible.
To call the imageView's setvisibility method, you will need a hanlder here.
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
image.setVisibiliy(VISIBLE);
Thread.sleep(200);
image.setVisibility(INVISIBLE);
}
});
I know this question has already been answered, but I thought I would add an answer for people who like me, stumbled across this looking for a similar result where the delay was caused by a process rather than a "sleep"
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
firstImage.setVisibility(ImageView.VISIBLE);
// Run the operation on a new thread
new Thread(new Runnable(){
public void run(){
myMethod();
returnVisibility();
}
}).start();
}
}
private void myMethod() {
// Perform the operation you wish to do before restoring visibility
}
private void returnVisibility() {
// Restore visibility to the object being run on the main UI thread.
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
firstImage.setVisibility(ImageView.INVISIBLE);
}
});
}