Niels Masdorp
Niels Masdorp

Reputation: 2428

Updating listview slows down the rest of the UI

I am developing a Android application that, among other things, includes a stopwatch. The stopwatch itself functions fine. It uses a Runnable to update the UI and show the time.

 private Runnable updateTimerThread = new Runnable() {

    public void run() {

        timeInMilliseconds = SystemClock.uptimeMillis() - startTime;

        updatedTime = timeSwapBuff + timeInMilliseconds;

        int secs = (int) (updatedTime / 1000);
        int mins = secs / 60;
        secs = secs % 60;
        int milliseconds = (int) (updatedTime % 1000);

        if(mins > 99){
            return;
        }

        mChronoMinutes.setText(mins + "");
        mChronoSeconds.setText(String.format("%02d", secs));
        mChronoMiliseconds.setText(String.format("%03d", milliseconds));

        customHandler.postDelayed(this, 0);

    }
};

But I want to include a lap timer. I have a button that, when pressed, adds the current time to my list and notifies my list adapter. There is some logic in the button's onClickListener but I figured it is not a big deal. This is the onClickListener:

View.OnClickListener mLapListener = new View.OnClickListener() {
    public void onClick(View v) {

        lapCounter++;

        int secs = (int) (updatedTime / 1000);
        int mins = secs / 60;
        secs = secs % 60;
        int milliseconds = (int) (updatedTime % 1000);

        //for laptime
        int lapSecs = (int) ((updatedTime - lastLapTime) / 1000);
        int lapMins = lapSecs / 60;
        lapSecs = lapSecs % 60;
        int lapMilliseconds = (int) ((updatedTime - lastLapTime) % 1000);

        laps.add(0, "# " + lapCounter + "   " + mins + " " + String.format("%02d", secs) + "," + String.format("%03d", milliseconds)
                + "   " + lapMins + " " + String.format("%02d", lapSecs) + "," + String.format("%03d", lapMilliseconds));

        adapter.notifyDataSetChanged();

        lapListView.post(new Runnable() {
            @Override
            public void run() {
                lapListView.smoothScrollToPosition(0);
            }
        });

        lastLapTime = updatedTime;

    }
};

As you can see it gets the current time and the time since the last lap and shows it in the listview. This works as expected, but the problem is each time I press the lap button my stopwatch itself starts to freeze, it is only nearly invisible with the first lap but every time I press the button the freezing becomes more noticable. After 20 laps the stopwatch hangs for half a second or so.

I figured maybe I am doing too much work on the UI-thread and I tried using a AsyncTask to do the logic in the doInBackground and update the listView in the onPostExecute method. This however did not make any difference, the timer still begins to freeze after a couple of laps.

I am a fairly new Android programmer and I don't really know what to do or what the problem actually is. I hope somebody out there can help me!

I found another thing, if I start clicking on the lap button fast enough then eventually it will FC and give me a "Fatal signal 6 (SIGABRT)" google tells me this is multithreading related..

Thanks in advance, Niels

EDIT:

This is the asynctask i've used, but that didn't change anything.

private class AddLapTask extends AsyncTask<Void, Void, Void>
{

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }
    @Override
    protected Void doInBackground(Void... params) {

        lapCounter++;

        secs = (int) (updatedTime / 1000);
        mins = secs / 60;
        secs = secs % 60;
        milliseconds = (int) (updatedTime % 1000);

        //for laptime
        lapSecs = (int) ((updatedTime - lastLapTime) / 1000);
        lapMins = lapSecs / 60;
        lapSecs = lapSecs % 60;
        lapMilliseconds = (int) ((updatedTime - lastLapTime) % 1000);

        lastLapTime = updatedTime;

        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);


        laps.add(0, "# " + lapCounter + "   " + mins + " " + String.format("%02d", secs) + "," + String.format("%03d", milliseconds)
                + "   " + lapMins + " " + String.format("%02d", lapSecs) + "," + String.format("%03d", lapMilliseconds));

        adapter.notifyDataSetChanged();

        lapListView.post(new Runnable() {
            @Override
            public void run() {
                lapListView.smoothScrollToPosition(0);
            }
        });

    }

}

And my ListAdapter:

public class LapListAdapter extends BaseAdapter {

private Context context;
private ArrayList<String> laps;

private int mSelectedItem;

public LapListAdapter(Context context, ArrayList<String> laps) {
    this.context = context;
    this.laps = laps;
}

@Override
public int getCount() {
    return laps.size();
}

@Override
public Object getItem(int position) {
    return laps.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

public int getSelectedItem() {
    return mSelectedItem;
}

public void setSelectedItem(int selectedItem) {
    mSelectedItem = selectedItem;
}


@Override
public View getView(int position, View convertView, ViewGroup parent) {

    Holder holder = new Holder();

    if (convertView == null) {
        LayoutInflater mInflater = (LayoutInflater)
                context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        convertView = mInflater.inflate(R.layout.lap_item, null);


        convertView.setTag(holder);
    } else {

        holder = (Holder) convertView.getTag();
    }

    holder.lap_info = (TextView) convertView.findViewById(R.id.lap_info);

    Typeface roboto;

    roboto = Typeface.createFromAsset(context.getAssets(), "fonts/Roboto-Bold.ttf");


    holder.lap_info.setText(laps.get(position));
    holder.lap_info.setTypeface(roboto);


    return convertView;
}

class Holder {

    TextView lap_info;

}

}

Hope it helps

Upvotes: 1

Views: 611

Answers (3)

Ifrit
Ifrit

Reputation: 6821

I bet it's your TypeFace.createFromAsset call that's causing the issue. Try taking that line out and seeing what happens.

You should be able to create that typeface in the adapter's constructor and just reuse it for the getView() method...instead of recreating it every time in the getView method.

Upvotes: 2

Devendra B. Singh
Devendra B. Singh

Reputation: 304

Please optimize you list view by using ViewHolder so that it will reuse the view and it will not create new every time it scrolls.

Do something like below to optemize your listView for better performance

if (convertView == null) {
convertView = inflater.inflate(layout, null, false);
holder = new Holder(convertView);
convertView.setTag(holder); // setting Holder as arbitrary object for row
}
else { // view recycling
// row already contains Holder object
holder = (Holder) convertView.getTag();
}
// set up row data from holder
titleText.setText(holder.getTitle().getText().toString());

public class Holder {

private View row;
private TextView title;

public Holder(View row) {
this.row = row;
}

public TextView getTitle() {
if (title == null) {
title = (TextView) row.findViewById(R.id.title);
}
return title;
}
}

Upvotes: 0

Sotti
Sotti

Reputation: 14389

AFAIK this shouldn't be made in the UI process. Try threads or asynctask. If you have tried the async task and still not working please paste your code or link to you project.

Upvotes: 0

Related Questions