From f7e34a707d8bf37f9b069c8e32188dd6d0adb83d Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Fri, 17 Jul 2015 10:16:14 -0700 Subject: [PATCH] Don't blow away entire recipient cache on clear event. Switch to marking recipients as "dirty" instead. // FREEBIE --- .../securesms/ConversationListActivity.java | 3 +- .../securesms/recipients/Recipient.java | 27 ++++++-- .../recipients/RecipientProvider.java | 62 +++++++++++++++---- .../securesms/recipients/Recipients.java | 22 ++++++- 4 files changed, 95 insertions(+), 19 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index a11e84e503..f2d9ca0a0e 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -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 diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java index 2ed3d9255b..09628ebabd 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java @@ -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 future) + Recipient(long recipientId, + @NonNull String number, + @Nullable Recipient stale, + @NonNull ListenableFutureTask 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() { @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; + } } diff --git a/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java b/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java index c77ae9b2f1..83374da6a0 100644 --- a/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java +++ b/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java @@ -51,9 +51,9 @@ public class RecipientProvider { private static final String TAG = RecipientProvider.class.getSimpleName(); - private static final Map recipientCache = Collections.synchronizedMap(new LRUCache(1000)); - private static final Map recipientsCache = Collections.synchronizedMap(new LRUCache(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 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 getRecipientDetailsAsync(final Context context, @@ -218,6 +218,46 @@ public class RecipientProvider { } } + private static class RecipientCache { + + private final Map 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 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(); + } + } + + } + } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/recipients/Recipients.java b/src/org/thoughtcrime/securesms/recipients/Recipients.java index 99149f2c88..15a23fcc49 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipients.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipients.java @@ -55,9 +55,10 @@ public class Recipients implements Iterable, RecipientModifiedListene private long mutedUntil = 0; private boolean blocked = false; private VibrateState vibrate = VibrateState.DEFAULT; + private boolean stale = false; Recipients() { - this(new LinkedList(), (RecipientsPreferences)null); + this(new LinkedList(), null); } Recipients(List recipients, @Nullable RecipientsPreferences preferences) { @@ -71,9 +72,19 @@ public class Recipients implements Iterable, RecipientModifiedListene } } - Recipients(List recipients, ListenableFutureTask preferences) { + Recipients(@NonNull List recipients, + @Nullable Recipients stale, + @NonNull ListenableFutureTask preferences) + { this.recipients = recipients; + if (stale != null) { + ringtone = stale.ringtone; + mutedUntil = stale.mutedUntil; + vibrate = stale.vibrate; + blocked = stale.blocked; + } + preferences.addListener(new FutureTaskListener() { @Override public void onSuccess(RecipientsPreferences result) { @@ -304,6 +315,13 @@ public class Recipients implements Iterable, RecipientModifiedListene } } + boolean isStale() { + return stale; + } + + void setStale() { + this.stale = true; + } public interface RecipientsModifiedListener { public void onModified(Recipients recipient);