I'm trying to start a countup timer once I start a new activity on a button click from the first activity, however I keep getting this error. Adding a delay doesn't seem to fix the issue as well and I don't want the delay to be there anyways as there will be an awkward pause.
public class CallActive extends AppCompatActivity {
TextView timerText;
Timer timer;
TimerTask timerTask;
Double time =0.0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getSupportActionBar().hide();
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_callactive);
Intent intent = getIntent();
String text = intent.getStringExtra(MainActivity.EXTRA_INFO);
TextView txtCaller = (TextView) findViewById(R.id.txtCaller2);
txtCaller.setText(text);
timerText = (TextView) findViewById(R.id.timerText);
timer = new Timer();
runOnUiThread(new Runnable() {
@Override
public void run() {
startTimer();
}
});
}
private void startTimer() {
timerTask = new TimerTask() {
@Override
public void run() {
time ;
timerText.setText(getTimerText());
}
};
timer.scheduleAtFixedRate(timerTask, 0, 1000);
}
private String getTimerText() {
int rounded = (int) Math.round(time);
int seconds = ((rounded % 86400) % 3600) % 60;
int minutes = ((rounded % 86400) % 3600) / 60;
return formatTime(seconds,minutes);
}
private String formatTime(int seconds, int minutes)
{
return String.format("d",minutes) " : " String.format("d",seconds);
}
}
I tried using runOnUIThread and Handlers but the same error persists
CodePudding user response:
run()
of your TimerTask
will be called on a background thread. So, your runOnUiThread()
needs to go inside of that run()
:
private void startTimer() {
timerTask = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
time ;
timerText.setText(getTimerText());
}
});
}
};
It would be simpler and more efficient to eliminate TimerTask
and Timer
and use postDelayed()
.
CodePudding user response:
You look like a little confused about UI elements and UI thread. All UI elements are created by the UI (or main) thread and they can only be modified by it. So although you start you timer using runOnUiThread
, this doesn't mean that the TimerTask
will run in the main thread. This is where you get confused.
// You define the timertask here but it will not run in this context which is the main thread context
timerTask = new TimerTask() {
@Override
public void run() {
time ;
// This textview is still will be touched from another thread that is not main thread
timerText.setText(getTimerText());
}
};
You must still execute the code that modifies the UI object only within the main thread like following:
timerTask = new TimerTask() {
@Override
public void run() {
time ;
runOnUiThread(()-> timerText.setText(getTimerText()));
}
};