Reputation: 193
I am experimenting with Threading in Android development by creating a stop watch. I currently have a very basic execution that seems to work just fine on the emulator, but when ran on my phone is anything but efficient (Pretty new Nexus 6 so the phone is fine). My question is, what is the most efficient way to update the UI thread continuously as a stop watch would need to do?
I am currently running it in an AsyncTask that updates the UI onProgressUpdate. I imagine an AsyncTask isn't the best choice, but I've also tried it in my own Runnable that ran even worse. I read about the Handler class, but am not confident in how to use it (or know if it's the answer). Below is where I am creating and executing my AsyncTask, and also the AsyncTask itself. I should note this is in a fragment.
// Where the thread kicks off on click
private class StartTimer implements View.OnClickListener{
@Override
public void onClick(View v) {
// passes the stopWacth object and the view to update
clockThread = new HiitClock(clockTv, stopWatch);
clockThread.execute();
}
}
Then this is my class that extends the AsyncTask
public class HiitClock extends AsyncTask<String, Void, String> {
private final String LOG_TAG = HiitClock.class.getSimpleName();
private TextView clock;
private StopWatch stopWatch;
public HiitClock(TextView clock, StopWatch sw){
this.clock = clock;
this.clockSegment = clockSegment;
this.roundSegment = roundSegment;
this.stopWatch = sw;
}
@Override
protected String doInBackground(String... params) {
stopWatch.start();
while(stopWatch.getState() == 1 || stopWatch.getState() == 2){
publishProgress();
}
return "done";
}
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
clock.setText(convertTime());
}
@Override
protected void onPostExecute(String result) {
// end of timer
clock.setText(result);
}
/*
* helper method for converting the time
* */
private String convertTime(){
long milli = stopWatch.getTime();
long sec = milli / 1000 ;
long min = sec / 60;
sec = sec % 60;
milli = milli % 100;
return String.format("%02d:%02d:%02d", min, sec, milli);
}
}
-------Update---------
I appreciate the responses. I've tried all suggestions and the performance is still terrible on a real device. I created a bare bones example below. Perhaps this bare bones version will help someone see what I am doing that is keeping the UI Thread from updating efficiently. It loops 100,000 times and uses a handler to try and update the UI thread. Thanks!
public class MainActivity extends Activity {
private RelativeLayout wrapper;
private TextView clockTv;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler();
wrapper = (RelativeLayout) findViewById(R.id.hitt_cardio_layout);
clockTv = (TextView) wrapper.findViewById(R.id.clock_text_view);
Button start = new Button(this);
start.setText("Start");
wrapper.addView(start);
start.setOnClickListener(new StartTimer());
}
private class StartTimer implements View.OnClickListener {
@Override
public void onClick(View v) {
new Thread(new Runner()).start();
}
}
private class Runner implements Runnable{
int i;
@Override
public void run() {
for(i = 0; i < 100000; i++){
handler.post(new Runnable() {
@Override
public void run() {
clockTv.setText("index: " + i);
}
});
}
}
}
}
Upvotes: 2
Views: 6105
Reputation: 1763
You can use onProgressUpdate() to update your UI. Apart from this there are two more ways of doing it:
Use Activity's runOnUiThread method:
Activity.runOnUiThread(new Runnable() {
@Override
public void run() {
//update your UI
}
});
You can also use Handler:
Handler h = new Handler(){
@Override
public void handleMessage(Message msg){
if(msg.what == 0){
updateUI();
}else{
showErrorDialog();
}
}
};
Upvotes: 0
Reputation: 39386
You can post runnable on the thread that updates the view:
clock.post(new Runnable() {
@Override public void run() {
clock.setText(convertTime());
// 50 millis to give the ui thread time to breath. Adjust according to your own experience
clock.postDelayed(this, 50);
}
});
(I omitted the stopping logic to keep the example short. Putting it back is a matter of testing and not reposting the runnable).
Upvotes: 1