Throttle the refresh of the message list as it changes (fixes #2126515)

* Catch onContentChanged and throttle calls to cursor.requery()
* Use 2.5s for now.  This seems to provide excellent responsiveness
  with little apparent latency.
* Also fixes #2135882

Change-Id: I9cab6558c9cfeb1dbdb5fb250f4f04059db324f7
This commit is contained in:
Marc Blank 2009-09-30 17:06:28 -07:00
parent b93860b5e4
commit 3850149239
2 changed files with 74 additions and 2 deletions

View File

@ -49,6 +49,8 @@ import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -72,6 +74,8 @@ import android.widget.AdapterView.OnItemClickListener;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
public class MessageList extends ListActivity implements OnItemClickListener, OnClickListener { public class MessageList extends ListActivity implements OnItemClickListener, OnClickListener {
// Intent extras (internal to this activity) // Intent extras (internal to this activity)
@ -1393,6 +1397,20 @@ public class MessageList extends ListActivity implements OnItemClickListener, On
private ColorStateList mTextColorPrimary; private ColorStateList mTextColorPrimary;
private ColorStateList mTextColorSecondary; private ColorStateList mTextColorSecondary;
// Timer to control the refresh rate of the list
private final RefreshTimer mRefreshTimer = new RefreshTimer();
// Last time we allowed a refresh of the list
private long mLastRefreshTime = 0;
// How long we want to wait for refreshes (a good starting guess)
// I suspect this could be lowered down to even 1000 or so, but this seems ok for now
private static final long REFRESH_INTERVAL_MS = 2500;
private Runnable mRefreshRunnable = new Runnable() {
public void run() {
mDataValid = mCursor.requery();
notifyDataSetChanged();
}
};
private java.text.DateFormat mDateFormat; private java.text.DateFormat mDateFormat;
private java.text.DateFormat mDayFormat; private java.text.DateFormat mDayFormat;
private java.text.DateFormat mTimeFormat; private java.text.DateFormat mTimeFormat;
@ -1400,7 +1418,7 @@ public class MessageList extends ListActivity implements OnItemClickListener, On
private HashSet<Long> mChecked = new HashSet<Long>(); private HashSet<Long> mChecked = new HashSet<Long>();
public MessageListAdapter(Context context) { public MessageListAdapter(Context context) {
super(context, null); super(context, null, true);
mContext = context; mContext = context;
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@ -1423,6 +1441,60 @@ public class MessageList extends ListActivity implements OnItemClickListener, On
mTimeFormat = android.text.format.DateFormat.getTimeFormat(context); // 12/24 time mTimeFormat = android.text.format.DateFormat.getTimeFormat(context); // 12/24 time
} }
/**
* We override onContentChange to throttle the refresh, which can happen way too often
* on syncing a large list (up to many times per second). This will prevent ANR's during
* initial sync and potentially at other times as well.
*/
@Override
protected synchronized void onContentChanged() {
if (mCursor != null && !mCursor.isClosed()) {
long sinceRefresh = SystemClock.elapsedRealtime() - mLastRefreshTime;
mRefreshTimer.schedule(REFRESH_INTERVAL_MS - sinceRefresh);
}
}
class RefreshTimer extends Timer {
private TimerTask timerTask = null;
protected void clear() {
timerTask = null;
}
protected synchronized void schedule(long delay) {
if (timerTask != null) return;
if (delay < 0) {
refreshList();
} else {
timerTask = new RefreshTimerTask();
schedule(timerTask, delay);
}
}
}
class RefreshTimerTask extends TimerTask {
@Override
public void run() {
refreshList();
}
}
/**
* Do the work of requerying the list and notifying the UI of changed data
* Make sure we call notifyDataSetChanged on the UI thread.
*/
private synchronized void refreshList() {
if (mCursor != null && !mCursor.isClosed()) {
if (Email.LOGD) {
Log.d("messageList", "refresh: "
+ (SystemClock.elapsedRealtime() - mLastRefreshTime) + "ms");
}
runOnUiThread(mRefreshRunnable);
}
mLastRefreshTime = SystemClock.elapsedRealtime();
mRefreshTimer.clear();
}
public Set<Long> getSelectedSet() { public Set<Long> getSelectedSet() {
return mChecked; return mChecked;
} }

View File

@ -1791,7 +1791,7 @@ public class SyncManager extends Service implements Runnable {
Mailbox m = Mailbox.restoreMailboxWithId(INSTANCE, mailboxId); Mailbox m = Mailbox.restoreMailboxWithId(INSTANCE, mailboxId);
if (syncError != null) { if (syncError != null) {
syncError.escalate(); syncError.escalate();
INSTANCE.log(m.mDisplayName + " now held for " + syncError.holdDelay + "s"); INSTANCE.log(m.mDisplayName + " held for " + syncError.holdDelay + "ms");
} else { } else {
errorMap.put(mailboxId, INSTANCE.new SyncError(exitStatus, false)); errorMap.put(mailboxId, INSTANCE.new SyncError(exitStatus, false));
INSTANCE.log(m.mDisplayName + " added to syncErrorMap, hold for 15s"); INSTANCE.log(m.mDisplayName + " added to syncErrorMap, hold for 15s");