Thread Stopping and Method execution - java

I am trying to have a thread that waits for user input for 3 seconds, if the user clicks the button execute "done" and if he doesn`t click in 3 seconds execute "not". I searched through stackoverflow but couldn't find an exact and simplified answer. Sorry if this is a repeat and/or noob question.
Here is the code I have for the moment but feel free to offer me another way, couldn't solve it with this;
private Thread thread;
TextView maintext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MainActivity myActivity = this;
maintext = (TextView) findViewById(R.id.mainText);
}
public void start(View view){
changeText("Waiting.");
thread= new Thread(){
#Override
public void run(){
try {
synchronized(this){
wait(3000);
}
}
catch(InterruptedException ex){
}
}
};
thread.start();
}
public void done(){
maintext.setText("You pressed the button in 3 seconds");
}
public void not(){
maintext.setText("You failed");
}
public void changeText(String text){
maintext.setText(text);
}
public void click(View view){
synchronized(thread){
thread.notifyAll();
}
}

How about a CountDownLatch?
Construction (in your class instance variable declaration, i.e. the place where thread is declared now):
CountDownLatch latch = new CountDownLatch(1);
In your waiting thread (called start), instead of creating and starting that Thread:
// wait for it to be notched or for timeout
boolean buttonPressed = latch.await(3, TimeUnit.SECONDS);
if (buttonPressed) {
....
}
And in your button click listener (that is not shown in your post, usually it's configured like clickButton.setOnClickListener( new OnClickListener() {):
latch.countDown();
Later addition
But please note that if that start() method is run in a UI thread, waiting on it will simply freeze the UI which is not what you want. Another approach would be to use something like events. Define two events: 'three seconds passed' and 'the button was pressed' and process them.
Instance variables:
private boolean buttonPressed = false;
private boolean expired = false;
private final Object monitor = new Object();
Timer initialization:
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
synchronized (monitor) {
if (!buttonPressed) {
// change text to 'You failed'
expired = true;
timer.cancel();
}
}
}
}, 3000);
In your button click listener:
synchronized (monitor) {
if (!expired) {
// change text to 'You pressed the button in 3 seconds'
buttonPressed = true;
timer.cancel();
}
}
There is no need to block any thread.

May be this answer will help you
private boolean isActive; //globally declare
changeText("Waiting.");
Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (!isActive) {
isActive = true;
}
}
};
handler.sendEmptyMessageDelayed(0,3000);
public void onClick(View v) {
if (!isActive) {
done();
} else {
not();
}
}
public void done() {
mainText.setText("You pressed the button in 3 seconds");
}
public void not() {
mainText.setText("You failed");
}
public void changeText(String text) {
mainText.setText(text);
}
I hope this will resolve your issue.

Related

Android - change UI immediately

After click button I would like to change its color, then wait one second and change its color back.
This is my code:
public void click(final View view) throws InterruptedException {
final Button btn = findViewById(view.getId());
btn.setBackgroundColor(Color.parseColor("#0000ff"));
btn.setClickable(false);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
t.join();
btn.setBackgroundColor(Color.parseColor("#e2e2e2"));
btn.setClickable(true);
}
It doesn't work. I've checked it with more complex code and debugger and it looks like all UI changes are made collectively after finish this function.
I've found this thread: apply ui changes immediately and tried to put setBackgroundColor() and setClickable() into runOnUiThread function:
runOnUiThread(new Runnable() {
#Override
public void run() {
btn.setBackgroundColor(Color.parseColor("#0000ff"));
btn.setClickable(false);
}
});
But it also doesn't work. What should I do?
Something like this :
private final Handler handler = new Handler();
public void click(final View view) {
view.setBackgroundColor(Color.parseColor("#0000ff"));
view.setClickable(false);
handler.postDelayed(() -> {
view.setBackgroundColor(Color.parseColor("#e2e2e2"));
view.setClickable(true);
}, 1000);
}
#Override protected void onPause() {
super.onPause();
handler.removeCallbacks(null);
}
The question is not very clear. However, I am trying to summarize the question that I have understood from your question.
You are trying to set a button's background color on clicking on it and change it back after some time. If this is the situation, then I think your idea of how threads work is wanting.
In your code, the button will change the color immediately as the sleep that you are using is running in another thread (other than UI thread). The code is executed correctly, however, you cannot see the effect of the Thread.sleep as its running in a separate thread.
So all you need to do here is to change the background color again inside the thread. Modify your code like the following.
public void click(final View view) throws InterruptedException {
final Button btn = findViewById(view.getId());
btn.setBackgroundColor(Color.parseColor("#0000ff"));
btn.setClickable(false);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
btn.setBackgroundColor(Color.parseColor("#e2e2e2"));
btn.setClickable(true);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
This should work.
I have created a demo trying to show what the code will do.
However, using Handler in case of updating UI elements in this specific case is recommended. Please see the comments below.
public void click(final View view) throws InterruptedException {
final Button btn = findViewById(view.getId());
btn.setBackgroundColor(Color.parseColor("#0000ff"));
btn.setClickable(false);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
btn.setBackgroundColor(Color.parseColor("#e2e2e2"));
btn.setClickable(true);
}
}, 1000);
}
Not sure why that wouldn't work, but I've done something similar with
delayHandler = new Handler();
delayHandler.postDelayed(new Runnable() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
//change stuff on ui
}
});
}
}, 1000);
if that doesn't work the only other functional difference in my code is that instead of btn being a final Button it's a private global variable in my activity.
Hope the following code will help :
btn.setBackgroundColor(Color.RED); // color you want for a second
new CountDownTimer(1000, 1000) {
#Override
public void onTick(long arg0) {
// TODO Auto-generated method stub
}
#Override
public void onFinish() {
btn.setBackgroundColor(Color.BLUE); //to change back color to prior state
}
}.start();
Try this,i think it's work for you..
final Button bPdf = findViewById(R.id.pdf);
bPdf.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
bPdf.setBackgroundColor(Color.parseColor("#0000ff"));
new CountDownTimer(1000, 50) {
#Override
public void onTick(long arg0) {
// TODO Auto-generated method stub
}
#Override
public void onFinish() {
bPdf.setBackgroundColor(Color.parseColor("#e2e2e2"));
}
}.start();
}
});

How to create a thread that lasts for a minute and takes input via button listener in android

I am working on an Android application in which I allow the users to enter text as many time they want in a single minute. But the thread is not terminating after a minute. My code:
public void startTimer(View view) throws Exception {
final Thread t = new Thread(new Runnable() {
final Button myButton = (Button) findViewById(R.id.myButton);
EditText mEdit = (EditText) findViewById(R.id.myTextInput);
#Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
myButton.setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v) {
Log.d("Tag", mEdit.getText().toString());
}
}
);
}
}
});
t.start();
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.schedule(new Runnable() {
#Override
public void run() {
t.interrupt();
}
},1, TimeUnit.MINUTES);
}
better use CountDownTimer http://developer.android.com/reference/android/os/CountDownTimer.html
In your solution you try do something with UI state in not main thread - this can be a issue.
Your thread isn't "taking input". It's continuously making and installing new onClick listeners.
You don't need a thread for what you're trying to do. You need a timer that goes off after one minute, and you need a listener that either accepts the input or rejects it depending on whether the timer has gone off yet or not.
You have two solutions you can do:
1st Solution:
You can simply use a handler.
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
t.interrupt();
}
}, 60000);
2nd Solution:
You can also use a CountDownTimer.
new CountDownTimer(60000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
t.interrupt();
}
}.start();

Android threads can't get notify() to work properly using wait() and notify()

So I am writing an Android application which will do a count down when the user presses a button. A thread runs the count down. My problem is that when I pause the application I want the thread to stop counting and then resume once the application is back. My notify is not working correctly. Help thanks!
public class MainActivity extends Activity {
private TextView mText;
private EditText mUserInput;
private CounterThread mCounterThread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = (TextView)findViewById(R.id.text);
mUserInput = (EditText)findViewById(R.id.userInput);
mCounterThread = new CounterThread();
}
#Override
public synchronized void onPause(){
super.onPause();
mCounterThread.running = false;
}
#Override
public synchronized void onResume(){
super.onResume();
mCounterThread.running = true;
notify();//seems like this does nothing!
}
public void startCounterThread(){
mCounterThread.start();
}
public void button_handler(View v){
startCounterThread();
}
public void updateSeconds(final long seconds){
Runnable UIdoWork = new Runnable(){
public void run(){
String time = String.valueOf(seconds);
mText.setText("Your file will open in " + time + " seconds");
}
};
runOnUiThread(UIdoWork);
}
private class CounterThread extends Thread{
int count = 10;
boolean running = true;
#Override
public synchronized void run(){
while(count != 0){
while(!running){
try{
wait();//wait() will wait forever
//I don't want to put a time since
//I have no clue when the user will resume again
}catch(InterruptedException e){
e.printStackTrace();
}
}
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
updateSeconds(count--);
}
}
}
Slightly modified code:
public class MainActivity extends Activity {
private TextView mText;
private EditText mUserInput;
private CounterThread mCounterThread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_2);
mText = (TextView) findViewById(R.id.text);
mUserInput = (EditText) findViewById(R.id.userInput);
mCounterThread = new CounterThread();
mCounterThread.start();
}
#Override
public synchronized void onPause() {
super.onPause();
mCounterThread.onPause();
}
#Override
public synchronized void onResume() {
super.onResume();
mCounterThread.onResume();
}
public void startCounterThread() {
mCounterThread.start();
}
public void button_handler(View v) {
startCounterThread();
}
public void updateSeconds(final long seconds) {
Runnable UIdoWork = new Runnable() {
public void run() {
String time = String.valueOf(seconds);
mText.setText("Your file will open in " + time + " seconds");
}
};
runOnUiThread(UIdoWork);
}
private class CounterThread extends Thread {
private int count = 10;
private final Object lock = new Object();
private volatile boolean isRunning = true;
public void onResume() {
if(!isRunning){
isRunning = true;
synchronized (lock){
lock.notify();
}
}
}
public void onPause() {
isRunning = false;
}
#Override
public void run() {
while (count != 0) {
synchronized (lock) {
if (!isRunning) try {
lock.wait();
} catch (InterruptedException e) {
//
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//
}
updateSeconds(count--);
}
}
}
}
your field running must be marked as volatile. It basically means that several threads could change it and all threads will see it.
do not expose monitor objects out of a Thread or a Runnable. It's quite bad idea to use activity as a monitor. It's quite bad idea to pass reference of activity anywhere.
you used different monitor objects: Thread and Activity. Use one inside thread.
It makes sense that is not working because wait() and notify() work over an object that is used as a lock. When you run wait() in your run() method, you are using an instance of CounterThread as a lock, but when you run notify() inside your onResume() method, you are using an instance of MainActivity. CounterThread will never get notified. Your alternative is (in your onResume() method):
...
synchronized(mCounterThread) {
mCounterThread.notify();
}
...

Android Thread for a timer

public class MainActivity extends Activity
{
int min, sec;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
min = 5;
sec = 0;
final TextView timer1 = (TextView) findViewById(R.id.timer1);
timer1.setText(min + ":" + sec);
Thread t = new Thread() {
public void run() {
sec-=1;
if (sec<0) {
min-=1;
sec=59;
}
timer1.setText(min + ":" + sec);
try
{
sleep(1000);
}
catch (InterruptedException e)
{}
}
};
t.start();
}
}
This is a code for a Thread in Java but it doesn't work. Can you help me?
Its a Timer that counts down from 5 Minutes to 0:00.
In your case you are using threads. So you cannot update ui from the thread other than the ui thread. SO you use runOnUithread. I would suggest you to use a countdown timer or a Handler.
1.CountDownTimer
http://developer.android.com/reference/android/os/CountDownTimer.html
Here's a link to another example. Suggest you to check the link for the count down timer.
Countdowntimer in minutes and seconds
Example:
public class MainActivity extends Activity {
Button b;
TextView tv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.textView1);
b= (Button) findViewById(R.id.button1);
b.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
startTimer(200000);
}
});
}
private void startTimer(long time){
CountDownTimer counter = new CountDownTimer(30000, 1000){
public void onTick(long millisUntilDone){
Log.d("counter_label", "Counter text should be changed");
tv.setText("You have " + millisUntilDone + "ms");
}
public void onFinish() {
tv.setText("DONE!");
}
}.start();
}
}
2.You can use a Handler
Example :
Handler m_handler;
Runnable m_handlerTask ;
int timeleft=100;
m_handler = new Handler();
m_handlerTask = new Runnable()
{
#Override
public void run() {
if(timeleft>=0)
{
// do stuff
Log.i("timeleft",""+timeleft);
timeleft--;
}
else
{
m_handler.removeCallbacks(m_handlerTask); // cancel run
}
m_handler.postDelayed(m_handlerTask, 1000);
}
};
m_handlerTask.run();
3.Timer
Timer runs on a different thread. You should update ui on the ui thread. use runOnUiThread
Example :
int timeleft=100;
Timer _t = new Timer();
_t.scheduleAtFixedRate( new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() //run on ui thread
{
public void run()
{
Log.i("timeleft",""+timeleft);
//update ui
}
});
if(timeleft>==0)
{
timeleft--;
}
else
{
_t.cancel();
}
}
}, 1000, 1000 );
You are trying to update the UI Thread from a background Thread with
timer1.setText(
which you can't do. You need to use runOnUiThread(), AsyncTask, CountDownTimer, or something similar.
See this answer for an example of runOnUiThread()
But CountDownTimer is nice for things like this.
Also, when posting a question on SO, statements like "it doesn't work." are very vague and often unhelpful. Please indicate the expected results compared to actual results of your code and logcat if the app is crashing.

How to stop code from running (with timer on ) until a button presses

Is there a way I can do this without having to create new timers and tasks? My code basically scans for wifi signals every 10 seconds. To ensure that the scan returns new results, I used another support class. Can someone verify that there aren't any obvious errors with this as well?
//Inside Button to start scanning
final int DELAY = 10000;
final Handler handler = new Handler();
chkScan.setOnClickListener(new View.OnClickListener() {
final Handler handler = new Handler();
ReceiverWifi = new WifiReceiver();
WIFI_Manager = new wifiScanner();
registerReceiver(receiverWifi, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
final Timer timer = new Timer();
final TimerTask task = new TimerTask() {
public void run() {
handler.post(new Runnable() {
public void run() {
mainWifi.startScan();
if ((WIFI_Manager.resultsAvailable())) {
processResults();
}
// to ensure results come from latest scan
// say there are no new results as of now
WIFI_Manager.waitForNextScan();
}
}
);
timer.schedule(task, 0, DELAY);
}
}
});
}
public void processResults() {
results = mainWifi.getScanResults();
WIFI_Manager.pause() //stop getting wifi results
//continue to process here
//
//
//I wish to put a button here, but and wait for user input before continuing
//but scans continue..
chkLabel.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
}
//more code
// ...
WIFI_Manager.resume();
}
class WifiReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context c, Intent intent) {
WIFI_Manager.getNewResults();
}
}
public class wifiScanner {
public wifiScanner() {
Pause = false;
new_results = false;
}
public boolean resultsAvailable() {
return new_results;
}
public void waitForNextScan() {
new_results = false;
}
public void getNewResults() {
new_results = true;
}
public boolean onPause() {
return Pause;
}
public void pause() {
unregisterReceiver(receiverWifi);
Pause = true;
}
public void resume() {
registerReceiver(receiverWifi, new IntentFilter(
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
Pause = false;
}
// pause: false- access to scan results are allowed
// pause: true- cannot access scan results
private boolean Pause;
// new_results: false- no new WIFI_resuls
// new_results: true - there are new results to be processed
private boolean new_results;
}
Instead of using a Timer, create a boolean field named stop, and use a while(!stop) loop with Thread.sleep(1000) at the end of an iteration. Your processResults() method will do stop = true; and in your restart button's listener do a stop=false;
But I think it's dirtier than use a Timer ;)

Categories

Resources