Don't blow away entire recipient cache on clear event.

Switch to marking recipients as "dirty" instead.

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2015-07-17 10:16:14 -07:00
parent 73bc7220db
commit f7e34a707d
4 changed files with 95 additions and 19 deletions

View File

@ -212,8 +212,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.w(TAG, "detected android contact data changed, refreshing cache");
// TODO only clear updated recipients from cache
Log.w(TAG, "Detected android contact data changed, refreshing cache");
RecipientFactory.clearCache();
ConversationListActivity.this.runOnUiThread(new Runnable() {
@Override

View File

@ -43,21 +43,32 @@ public class Recipient {
private final long recipientId;
private String number;
private String name;
private String number;
private String name;
private boolean stale;
private ContactPhoto contactPhoto;
private Uri contactUri;
@Nullable private MaterialColor color;
Recipient(long recipientId, String number, ListenableFutureTask<RecipientDetails> future)
Recipient(long recipientId,
@NonNull String number,
@Nullable Recipient stale,
@NonNull ListenableFutureTask<RecipientDetails> future)
{
this.recipientId = recipientId;
this.number = number;
this.contactPhoto = ContactPhotoFactory.getLoadingPhoto();
this.color = null;
if (stale != null) {
this.name = stale.name;
this.contactUri = stale.contactUri;
this.contactPhoto = stale.contactPhoto;
this.color = stale.color;
}
future.addListener(new FutureTaskListener<RecipientDetails>() {
@Override
public void onSuccess(RecipientDetails result) {
@ -76,7 +87,7 @@ public class Recipient {
@Override
public void onFailure(Throwable error) {
Log.w("Recipient", error);
Log.w(TAG, error);
}
});
}
@ -174,4 +185,12 @@ public class Recipient {
public interface RecipientModifiedListener {
public void onModified(Recipient recipient);
}
boolean isStale() {
return stale;
}
void setStale() {
this.stale = true;
}
}

View File

@ -51,9 +51,9 @@ public class RecipientProvider {
private static final String TAG = RecipientProvider.class.getSimpleName();
private static final Map<Long,Recipient> recipientCache = Collections.synchronizedMap(new LRUCache<Long,Recipient>(1000));
private static final Map<RecipientIds,Recipients> recipientsCache = Collections.synchronizedMap(new LRUCache<RecipientIds, Recipients>(1000));
private static final ExecutorService asyncRecipientResolver = Util.newSingleThreadedLifoExecutor();
private static final RecipientCache recipientCache = new RecipientCache();
private static final RecipientsCache recipientsCache = new RecipientsCache();
private static final ExecutorService asyncRecipientResolver = Util.newSingleThreadedLifoExecutor();
private static final String[] CALLER_ID_PROJECTION = new String[] {
PhoneLookup.DISPLAY_NAME,
@ -64,23 +64,23 @@ public class RecipientProvider {
Recipient getRecipient(Context context, long recipientId, boolean asynchronous) {
Recipient cachedRecipient = recipientCache.get(recipientId);
if (cachedRecipient != null) return cachedRecipient;
if (cachedRecipient != null && !cachedRecipient.isStale()) return cachedRecipient;
String number = CanonicalAddressDatabase.getInstance(context).getAddressFromId(recipientId);
if (asynchronous) {
cachedRecipient = new Recipient(recipientId, number, getRecipientDetailsAsync(context, recipientId, number));
cachedRecipient = new Recipient(recipientId, number, cachedRecipient, getRecipientDetailsAsync(context, recipientId, number));
} else {
cachedRecipient = new Recipient(recipientId, getRecipientDetailsSync(context, recipientId, number));
}
recipientCache.put(recipientId, cachedRecipient);
recipientCache.set(recipientId, cachedRecipient);
return cachedRecipient;
}
Recipients getRecipients(Context context, long[] recipientIds, boolean asynchronous) {
Recipients cachedRecipients = recipientsCache.get(new RecipientIds(recipientIds));
if (cachedRecipients != null) return cachedRecipients;
if (cachedRecipients != null && !cachedRecipients.isStale()) return cachedRecipients;
List<Recipient> recipientList = new LinkedList<>();
@ -88,16 +88,16 @@ public class RecipientProvider {
recipientList.add(getRecipient(context, recipientId, asynchronous));
}
if (asynchronous) cachedRecipients = new Recipients(recipientList, getRecipientsPreferencesAsync(context, recipientIds));
if (asynchronous) cachedRecipients = new Recipients(recipientList, cachedRecipients, getRecipientsPreferencesAsync(context, recipientIds));
else cachedRecipients = new Recipients(recipientList, getRecipientsPreferencesSync(context, recipientIds));
recipientsCache.put(new RecipientIds(recipientIds), cachedRecipients);
recipientsCache.set(new RecipientIds(recipientIds), cachedRecipients);
return cachedRecipients;
}
void clearCache() {
recipientCache.clear();
recipientsCache.clear();
recipientCache.reset();
recipientsCache.reset();
}
private @NonNull ListenableFutureTask<RecipientDetails> getRecipientDetailsAsync(final Context context,
@ -218,6 +218,46 @@ public class RecipientProvider {
}
}
private static class RecipientCache {
private final Map<Long,Recipient> cache = new LRUCache<>(1000);
public synchronized Recipient get(long recipientId) {
return cache.get(recipientId);
}
public synchronized void set(long recipientId, Recipient recipient) {
cache.put(recipientId, recipient);
}
public synchronized void reset() {
for (Recipient recipient : cache.values()) {
recipient.setStale();
}
}
}
private static class RecipientsCache {
private final Map<RecipientIds,Recipients> cache = new LRUCache<>(1000);
public synchronized Recipients get(RecipientIds ids) {
return cache.get(ids);
}
public synchronized void set(RecipientIds ids, Recipients recipients) {
cache.put(ids, recipients);
}
public synchronized void reset() {
for (Recipients recipients : cache.values()) {
recipients.setStale();
}
}
}
}

View File

@ -55,9 +55,10 @@ public class Recipients implements Iterable<Recipient>, RecipientModifiedListene
private long mutedUntil = 0;
private boolean blocked = false;
private VibrateState vibrate = VibrateState.DEFAULT;
private boolean stale = false;
Recipients() {
this(new LinkedList<Recipient>(), (RecipientsPreferences)null);
this(new LinkedList<Recipient>(), null);
}
Recipients(List<Recipient> recipients, @Nullable RecipientsPreferences preferences) {
@ -71,9 +72,19 @@ public class Recipients implements Iterable<Recipient>, RecipientModifiedListene
}
}
Recipients(List<Recipient> recipients, ListenableFutureTask<RecipientsPreferences> preferences) {
Recipients(@NonNull List<Recipient> recipients,
@Nullable Recipients stale,
@NonNull ListenableFutureTask<RecipientsPreferences> preferences)
{
this.recipients = recipients;
if (stale != null) {
ringtone = stale.ringtone;
mutedUntil = stale.mutedUntil;
vibrate = stale.vibrate;
blocked = stale.blocked;
}
preferences.addListener(new FutureTaskListener<RecipientsPreferences>() {
@Override
public void onSuccess(RecipientsPreferences result) {
@ -304,6 +315,13 @@ public class Recipients implements Iterable<Recipient>, RecipientModifiedListene
}
}
boolean isStale() {
return stale;
}
void setStale() {
this.stale = true;
}
public interface RecipientsModifiedListener {
public void onModified(Recipients recipient);