Fix contact cache rebuilding
Don't toss and rebuild the ContactInfoCache on every contact change.
With this change, we mark a contact as stale and the next time we're
asked for the contact, we asynchronously update the contact in the
background. Helps speedup bug 2167799.
Change-Id: Idc1271776cddd43fedcd7f31608a3302c8861813
diff --git a/src/com/android/mms/data/Contact.java b/src/com/android/mms/data/Contact.java
index 054e2ac..774c6c9 100644
--- a/src/com/android/mms/data/Contact.java
+++ b/src/com/android/mms/data/Contact.java
@@ -21,7 +21,6 @@
import com.android.mms.ui.MessageUtils;
import com.android.mms.util.ContactInfoCache;
import com.android.mms.util.TaskStack;
-import com.android.mms.util.AddressUtils;
import com.android.mms.LogTag;
public class Contact {
@@ -30,15 +29,15 @@
private static final TaskStack sTaskStack = new TaskStack();
- private static final ContentObserver sContactsObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfUpdate) {
- if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
- log("contact changed, invalidate cache");
- }
- invalidateCache();
- }
- };
+// private static final ContentObserver sContactsObserver = new ContentObserver(new Handler()) {
+// @Override
+// public void onChange(boolean selfUpdate) {
+// if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
+// log("contact changed, invalidate cache");
+// }
+// invalidateCache();
+// }
+// };
private static final ContentObserver sPresenceObserver = new ContentObserver(new Handler()) {
@Override
@@ -60,6 +59,7 @@
private int mPresenceResId; // TODO: make this a state instead of a res ID
private String mPresenceText;
private BitmapDrawable mAvatar;
+ private boolean mIsStale;
@Override
public synchronized String toString() {
@@ -78,6 +78,7 @@
mLabel = "";
mPersonId = 0;
mPresenceResId = 0;
+ mIsStale = true;
}
private static void logWithTrace(String msg, Object... format) {
@@ -114,6 +115,8 @@
if (contact == null) {
contact = new Contact(number);
Cache.put(contact);
+ }
+ if (contact.mIsStale) {
asyncUpdateContact(contact, canBlock);
}
return contact;
@@ -123,17 +126,21 @@
if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
log("invalidateCache");
}
-
+
// force invalidate the contact info cache, so we will query for fresh info again.
// This is so we can get fresh presence info again on the screen, since the presence
// info changes pretty quickly, and we can't get change notifications when presence is
// updated in the ContactsProvider.
ContactInfoCache.getInstance().invalidateCache();
- // Queue updates for the whole cache.
- sTaskStack.clear();
-
- asyncUpdateContacts(Cache.getContacts(), false);
+ // While invalidating our local Cache doesn't remove the contacts, it will mark them
+ // stale so the next time we're asked for a particular contact, we'll return that
+ // stale contact and at the same time, fire off an asyncUpdateContact to update
+ // that contact's info in the background. UI elements using the contact typically
+ // call addListener() so they immediately get notified when the contact has been
+ // updated with the latest info. They redraw themselves when we call the
+ // listener's onUpdate().
+ Cache.invalidate();
}
private static String emptyIfNull(String s) {
@@ -184,7 +191,7 @@
}
return false;
}
-
+
private static void asyncUpdateContact(final Contact c, boolean canBlock) {
if (c == null) {
return;
@@ -207,30 +214,11 @@
}
}
- private static void asyncUpdateContacts(final List<Contact> contacts, boolean canBlock) {
- Runnable r = new Runnable() {
- public void run() {
- if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
- log("asyncUpdateContacts...");
- }
-
- for (Contact c : contacts) {
- updateContact(c);
- }
- }
- };
-
- if (canBlock) {
- r.run();
- } else {
- sTaskStack.push(r);
- }
- }
-
private static void updateContact(final Contact c) {
if (c == null) {
return;
}
+ c.mIsStale = false;
// Check to see if this is the local ("me") number.
if (handleLocalNumber(c)) {
@@ -462,6 +450,16 @@
return new ArrayList<Contact>(sInstance.mCache);
}
}
+
+ static void invalidate() {
+ // Don't remove the contacts. Just mark them stale so we'll update their
+ // info, particularly their presence.
+ synchronized (sInstance) {
+ for (Contact c : sInstance.mCache) {
+ c.mIsStale = true;
+ }
+ }
+ }
}
private static void log(String msg) {
diff --git a/src/com/android/mms/util/ContactInfoCache.java b/src/com/android/mms/util/ContactInfoCache.java
index 77320a5..58dc121 100644
--- a/src/com/android/mms/util/ContactInfoCache.java
+++ b/src/com/android/mms/util/ContactInfoCache.java
@@ -20,17 +20,13 @@
import com.android.mms.ui.MessageUtils;
import com.google.android.mms.util.SqliteWrapper;
-import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
-import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
-import android.os.Handler;
-import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Presence;
@@ -43,9 +39,7 @@
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
@@ -62,7 +56,6 @@
private static final boolean LOCAL_DEBUG = false;
- private static final long REBUILD_DELAY = 5000; // 5 seconds
private static final String SEPARATOR = ";";
// query params for caller id lookup
@@ -182,19 +175,6 @@
private ContactInfoCache(Context context) {
mContext = context;
-
- ContentResolver resolver = context.getContentResolver();
- resolver.registerContentObserver(Data.CONTENT_URI, true,
- new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfUpdate) {
- synchronized (mCacheRebuildLock) {
- mPhoneCacheInvalidated = true;
- mEmailCacheInvalidated = true;
- startCacheRebuilder();
- }
- }
- });
}
/**
@@ -524,100 +504,6 @@
return entry;
}
- /**
- * Start the background cache rebuilding thread if there is not one yet.
- */
- private void startCacheRebuilder() {
- if (mCacheRebuilder == null) {
- mCacheRebuilder = new Thread(new Runnable() {
- public void run() {
- rebuildCache();
- }
- });
- mCacheRebuilder.start();
- }
- }
-
- /**
- * Get the list of phone/email candidates for the cache rebuilding. This is
- * a snapshot of the keys in the cache.
- */
- private void getRebuildList(List<String> phones, List<String> emails) {
- synchronized (mCache) {
- for (String name : mCache.keySet()) {
- if (Mms.isEmailAddress(name)) {
- if (emails != null) {
- emails.add(name);
- }
- } else {
- if (phones != null) {
- phones.add(name);
- }
- }
- }
- }
- }
-
- /**
- * The actual work of rebuilding the cache, i.e. syncing our cache with
- * the contacts database.
- */
- private void rebuildCache() {
- List<String> phones;
- List<String> emails;
-
- for (;;) {
- // simulate the Nagle's algorithm:
- // delay for a while to prevent from getting too busy, when, say,
- // there is a big contacts sync going on
- try {
- Thread.sleep(REBUILD_DELAY);
- } catch (InterruptedException ie) {
- }
-
- phones = null;
- emails = null;
- synchronized (mCacheRebuildLock) {
- // if nothing changed during our sync, stop this thread
- // otherwise, just keep working on it.
- if (!(mPhoneCacheInvalidated || mEmailCacheInvalidated)) {
- mCacheRebuilder = null;
- return;
- }
- if (mPhoneCacheInvalidated) {
- phones = new ArrayList<String>();
- mPhoneCacheInvalidated = false;
- }
- if (mEmailCacheInvalidated) {
- emails = new ArrayList<String>();
- mEmailCacheInvalidated = false;
- }
- }
- // retrieve the list of phone/email candidates for syncing
- // which is a snapshot of the keys in the cache
- getRebuildList(phones, emails);
- // now sync
- if (phones != null) {
- if (LOCAL_DEBUG) log("rebuild cache for phone numbers...");
- for (String phone : phones) {
- CacheEntry entry = queryContactInfoByNumber(phone);
- synchronized (mCache) {
- mCache.put(phone, entry);
- }
- }
- }
- if (emails != null) {
- if (LOCAL_DEBUG) log("rebuild cache for emails...");
- for (String email : emails) {
- CacheEntry entry = queryEmailDisplayName(email);
- synchronized (mCache) {
- mCache.put(email, entry);
- }
- }
- }
- }
- }
-
private void log(String msg) {
Log.d(TAG, "[ContactInfoCache] " + msg);
}