Can't get value which is changed inside Runnable - java

I have a code like this:
time = 5;
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
if(time == 0)
time = 0;
else {
time --;
}
handler.postDelayed(this, 1000);
}
};
My application contains a button which needs to collect value of the 'time' but the problem is when I click the button, value of the 'time' is always 5 no matter when I click the button. I tested it and 'time' really decreases by one but I can't get that value.
How can I get real value of the 'time' variable after I click a button?

Create a thread for your handler to handle. Have the thread run in the background, but have it pass it's information back to your handler via a message bundle. Something like this
private class HandlerThread extends Thread{
private Handler handler;
public HandlerThread(Handler uiHandler){
handler = uiHandler;
}
public void run(){
int time = 5;
Bundle b;
for(int i = 0; i < time; i++){
b = new Bundle();
b.putInt("time_key", time);
Message message = handler.obtainMessage();
message.setData(b);
handler.postDelayed(this, 1000);
handler.sendMessage(message);
}
}
}
And then have your handler handle the message
final Handler handler = new Handler(){
public void handleMessage(Message msg){
Bundle b;
b = msg.getData();
mainUiTime = b.getInt("time_key",0))
}
};
You can start your background thread like this
HandlerThread handlerThread = new HandlerThread(handler);
handlerThread.start();
As a note, you will also need to figure out a way of canceling your thread once it is done running. This is usually done by creating a boolean variable, something like keepRunning, and put it in your run() method as a while loop; while(keepRunning). Then in your mainUi change the variable to false when you are out of buttons so that it essentially terminates the running portion of the thread.

Related

Thread issues with handler in android

One part of my activity needs two timers running at once, one of them using Handler and the other using CountDownTimer. The handler portion updates the display every second, while the CountDownTimer counts down to when this part of the code ends. For some reason, I cannot run these both at the same time, and even when I commented out the CountDownTimer portion, the handler portion only ran once, instead of repeating every second. I am at a loss for what to do here. Any ideas? The relevant part of the code is below. For some clarification, the first handler seen here is supposed to run on its own, until a condition is met, at which point it reruns the function and goes to the second if statement. Seen within the second if statement are the details I mentioned at the beginning.
private void statusCheck() {
if (possible = true) {
final Random random = new Random();
final Handler handler1 = new Handler();
final int delay1 = 1000; //milliseconds
handler1.postDelayed(new Runnable() {
public void run() {
runChance = random.nextInt(1000);
if (runChance < 100) {
possible = false;
statusCheck();
}
}
}, delay1);
}
if (possible = false) {
final Handler Handler = new Handler();
final int Delay = 1000; //milliseconds
Handler.postDelayed(new Runnable() {
public void run() {
// code to update every second
}
}, Delay);
new CountDownTimer(ghostDuration, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
//code to run when finished
}
}.start();
}
}
First of all it's not a good idea to to run timer and tasks under Android that way.
Android will block these timer and tasks because they work against the framework.
If you want to implement it correctly with a WorkManager watch out this video from Google which gives you some fundamentals: Android Jetpack WorkManager

Android wont let me enter to an endless loop

Hi im trying to do an app that searchs bluetooth devices that are near every 5-10 seconds, so I tried to do an endless loop but the app is getting stuck when I press the button that starts the loop:
int stopInt=2;
serachB.setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v) {
do {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
listAdapter.clear();
getPairedDevices();
startDiscovery();
}
}, 4000);
} while (stopInt>1);
}
private final Handler mMyhandler = new Handler();
private final Runnable checkBluetoothRunnable = new Runnable() {
public void run() {
//Do your work here
mMyHandler.postDelayed(this, 5000);
}
};
serachB.setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v){
mMyhandler.post(checkBluetoothRunnable);
}
}
}
Change it this way:
serachB.setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v) {
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
while(true){
listAdapter.clear();
getPairedDevices();
startDiscovery();
Thread.sleep(4000);
}
}
});
}}
You are continually creating new instances of Handler in your while loop which is going to eat up memory. I wouldn't be surprised if you were getting an OutOfMemoryError which is causing the termination. Try creating a single instance outside of your loop.
The Runnable passed to Handler runs on the UIThread since it was created on that thread. This should be avoided for lengthy operations and instead delegated to another thread.
When you create a new Handler, it is bound to the thread / message
queue of the thread that is creating it
Also, rather than using the Handler class you could potentially swap it out to use ScheduledThreadPoolExecutor which will give you a pool of threads which can then periodically execute your device discovery code.
Firstly create a ScheduledExecutorService:
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
Create an instance of your Runnable:
MyRunnable deviceDiscovery = new MyRunnable();
Then in your onClick method you kick off with scheduleAtFixedRate:
ScheduledFuture<?> future = scheduledExecutorService.scheduleAtFixedRate(deviceDiscovery, 0, 1, TimeUnit.MINUTES);
Then when you want to stop it running you need to call cancel on the ScheduledFuture:
future.cancel(true); // Set to true to say it can interrupt if already running

how to run TimerTask off main UI thread?

I am having trouble with a TimerTask Interfering with In App Purchasing (Async Tasks).
I am weak with Threads, so I believe it is running on the main UI thread, eating up resources.
How can I run this outside the UI thread? I have searched, and tried some suggestions using handlers. but seems like I get the same result, app gets really laggy.
when I don't run this task (refreshes every 500mS), the activity runs smoothly, and there are no hangs during In app purchases.
Your help is appreciated, code snippet below:
public class DummyButtonClickerActivity extends Activity {
protected Timer timeTicker = new Timer("Ticker");
private Handler timerHandler = new Handler();
protected int timeTickDown = 20;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainhd);
// start money earned timer handler
TimerTask tick = new TimerTask() {
public void run() {
myTickTask();
}
};
timeTicker.scheduleAtFixedRate(tick, 0, 500); // 500 ms each
} // End OnCreate
protected void myTickTask() {
if (timeTickDown == 0) {
/// run my code here
//total = total + _Rate;
timerHandler.post(doUpdateTimeout);
}
else if(timeTickDown < 0) {
// do nothing
}
timeTickDown--;
}
private Runnable doUpdateTimeout = new Runnable() {
public void run() {
updateTimeout();
}
};
private void updateTimeout() {
// reset tick
timeTickDown = 2; // 2* 500ms == once a second
}
}
You can use HandlerThread that will run your Handler on a separate Thread
documentation:
Handy class for starting a new thread that has a looper.
The looper can then be used to create handler classes. Note that start() must still be called.
example:
HandlerThread mHandlerThread = new HandlerThread("my-handler");
mHandlerThread.start();
Handler mHandler = new Handler(mHandlerThread.getLooper());
update:
private Runnable doUpdateTimeout;
private HandlerThread mHandlerThread;
private Handler timerHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainhd);
// start money earned timer handler
TimerTask tick = new TimerTask() {
public void run() {
myTickTask();
}
};
mHandlerThread = new HandlerThread("my-handler");
mHandlerThread.start();
timerHandler = new Handler(mHandlerThread.getLooper());
doUpdateTimeout = new Runnable() {
public void run() {
updateTimeout();
}
};
timeTicker.scheduleAtFixedRate(tick, 0, 500); // 500 ms each
} // End OnCreate
protected void myTickTask() {
if (timeTickDown == 0) {
/// run my code here
//total = total + _Rate;
timerHandler.post(doUpdateTimeout);
}
else if(timeTickDown < 0) {
// do nothing
}
timeTickDown--;
}
private void updateTimeout() {
// reset tick
timeTickDown = 2; // 2* 500ms == once a second
}
}
when you want to update the TextView from different thread
call this:
YOU_ACITIVITY_CLASS.this.runOnUiThread(new Runnable() {
#Override
public void run() {
//update here
}
});
The handler is running and processing in the main ui thread,so the method doUpdateTimeout will be executed in the main thread.
In your code, after running 10 seconds later,the timeTickDown equals to 0 and code timerHandler.post(doUpdateTimeout); will be invoked,which will be executed in the main thread. Because it just let timeTickDown = 2; one second later,this code will be executed again(in the main ui thread) and then go on in each second.If there is some other code in doUpdateTimeout or updateTimeout,your main thread will be laggy.
Just change timerHandler.post(doUpdateTimeout); to updateTimeout()(call it directly and then execute it in the Timer thread,not the main ui thread).

If function appending loading message more than once

I have a loading screen, I want to display the loadingMessage1 for 3 secs than shows loading message 2. I only want loading message 2 to appear once, but when I tried to do it, loading message 2 kept getting appended in an endless loop.
So i tried put a count variable to increases after every loading message 2 append, but it doesnt work. where am i going wrong, is there another solution to this?
final TextView loadingMessage1 = (TextView)this.findViewById(R.id.loadingMessage1);
int count = 0;
final Handler handler = new Handler();
if (count == 0){
handler.post(new Runnable(){
#Override
public void run(){
loadingMessage1.append("Loading Message 2");
handler.postDelayed(this, 3*1000L);
}
});
count++;
}
You are making a mistake on the following line:
handler.postDelayed(this, 3*1000L);
Here you send to this Runnable object to the handler while handling this. That means when you fist execute the Runnable, on the end you add it again to the handler's message loop. Thus you obtained an infinite loop. Something of the like will solve your problem:
final TextView loadingMessage1 = (TextView)this.findViewById(R.id.loadingMessage1);
int count = 0;
final Handler handler = new Handler();
handler.postDelayed(new Runnable(){
#Override
public void run(){
loadingMessage1.append("Loading Message 2");
}
}, 3*1000L);
EDIT:
To make all this stuff flexible, do the following:
// inner class
class TextChanger implements Runnable {
private final String message;
public TextChanger(String message) {
this.message = message;
}
#Override
public void run() {
loadingMessage1.append(message);
}
}
and then somewhere in your code:
public static final long TIME_CONSTANT = 3*1000L;
final TextView loadingMessage1 = (TextView)this.findViewById(R.id.loadingMessage1);
int count = 0;
final Handler handler = new Handler();
handler.postDelayed(new TextChanger("Whatever 1"), TIME_CONSTANT);
handler.postDelayed(new TextChanger("Whatever 2"), 2 * TIME_CONSTANT);
and so on. The trick is not to reference this as you did before, because it creates an infinite loop.
You need to avoid posting another event after each execution. Perhaps something more like this:
void showMessage1() {
// ... code to show message 1 ...
handler.postDelayed(new Runnable(){
#Override
public void run(){
showMessage2();
}
}, 3*1000L);
}
void showMessage2() {
// ... code to show message 2 ...
}
Note that message-showing code only executes once per message.

Run loop every second java

int delay = 1000; // delay for 1 sec.
int period = 10000; // repeat every 10 sec.
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask()
{
public void run()
{
displayData(); // display the data
}
}, delay, period);
And other:
while(needToDisplayData)
{
displayData(); // display the data
Thread.sleep(10000); // sleep for 10 seconds
}
Both of them doesn't work (application is force closed). What other options I can try?
You code is failed because you perform sleep in background thread but display data must be performed in UI thread.
You have to run displayData from runOnUiThread(Runnable) or define handler and send message to it.
for example:
(new Thread(new Runnable()
{
#Override
public void run()
{
while (!Thread.interrupted())
try
{
Thread.sleep(1000);
runOnUiThread(new Runnable() // start actions in UI thread
{
#Override
public void run()
{
displayData(); // this action have to be in UI thread
}
});
}
catch (InterruptedException e)
{
// ooops
}
}
})).start(); // the while thread will start in BG thread
Use onPostDelayed() accessed from any of your View or a Handler. You save memory by not creating a Timer or new Thread.
private final Handler mHandler = new Handler();
private final Runnable mUpdateUI = new Runnable() {
public void run() {
displayData();
mHandler.postDelayed(mUpdateUI, 1000); // 1 second
}
}
};
mHandler.post(mUpdateUI);
Try this :
#Override
public void run() {
TextView tv1 = (TextView) findViewById(R.id.tv);
while(true){
showTime(tv1);
try {
Thread.sleep(1000);
}catch (Exception e) {
tv1.setText(e.toString());
}
}
}
U can also try this
There is an another way also that you can use to update the UI on specific time interval. Above two options are correct but depends on the situation you can use alternate ways to update the UI on specific time interval.
First declare one global varialbe for Handler to update the UI control from Thread, like below
Handler mHandler = new Handler();
Now create one Thread and use while loop to periodically perform the task using the sleep method of the thread.
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
while (true) {
try {
Thread.sleep(10000);
mHandler.post(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
// Write your code here to update the UI.
}
});
} catch (Exception e) {
// TODO: handle exception
}
}
}
}).start();
There several mistakes you have done:
You should never invoke Thread.sleep() on the main thread (and you should never block it for a long time as well). Once main thread is blocked for more then 5 seconds, an ANR (application not responding) happens and it is force closed.
You should avoid using Timer in android. Try Handler instead. Good thing about handler is that it is created on the main thread -> can access Views (unlike Timer, which is executed on its own thread, which cannot access Views).
class MyActivity extends Activity {
private static final int DISPLAY_DATA = 1;
// this handler will receive a delayed message
private Handler mHandler = new Handler() {
#Override
void handleMessage(Message msg) {
if (msg.what == DISPLAY_DATA) displayData();
}
};
#Override
void onCreate(Bundle b) {
//this will post a message to the mHandler, which mHandler will get
//after 5 seconds
mHandler.postEmptyMessageDelayed(DISPLAY_DATA, 5000);
}
}
I came across this thread when i tried to get around the problem that you can't hide seconds in DigitalClock widget for Android. DigitalClock is deprecated now and the recommended widget to use now is TextClock. That don't work on old APIs tho... Therefore i had to write my own 24 hour clock. I don't know if this is a good implementation but it seems to work (and it is updated every second):
import java.util.Calendar;
import android.os.Handler;
import android.view.View;
import android.widget.TextView;
/**
* A 24 hour digital clock represented by a TextView
* that can be updated each second. Reads the current
* wall clock time.
*/
public class DigitalClock24h {
private TextView mClockTextView; // The textview representing the 24h clock
private boolean mShouldRun = false; // If the Runnable should keep on running
private final Handler mHandler = new Handler();
// This runnable will schedule itself to run at 1 second intervals
// if mShouldRun is set true.
private final Runnable mUpdateClock = new Runnable() {
public void run() {
if(mShouldRun) {
updateClockDisplay(); // Call the method to actually update the clock
mHandler.postDelayed(mUpdateClock, 1000); // 1 second
}
}
};
/**
* Creates a 24h Digital Clock given a TextView.
* #param clockTextView
*/
public DigitalClock24h(View clockTextView) {
mClockTextView = (TextView) clockTextView;
}
/**
* Start updating the clock every second.
* Don't forget to call stopUpdater() when you
* don't need to update the clock anymore.
*/
public void startUpdater() {
mShouldRun = true;
mHandler.post(mUpdateClock);
}
/**
* Stop updating the clock.
*/
public void stopUpdater() {
mShouldRun = false;
}
/**
* Update the textview associated with this
* digital clock.
*/
private void updateClockDisplay() {
Calendar c = Calendar.getInstance();
int hour = c.get(Calendar.HOUR_OF_DAY); // 24 hour
int min = c.get(Calendar.MINUTE);
String sHour;
String sMin;
if(hour < 10) {
sHour = "0" + hour;
} else sHour = "" + hour;
if(min < 10) {
sMin = "0" + min;
} else sMin = "" + min;
mClockTextView.setText(sHour + ":" + sMin);
}
}
Thankyou biegleux for pointing me in the, i suppose, correct direction!

Categories

Resources