Replace LinkedHashMap with a SoftReference LRUCache. Add Slide cache.

pull/1/head
Moxie Marlinspike 11 years ago
parent a362c8755a
commit 50fae64330

@ -31,9 +31,11 @@ import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.LRUCache;
import java.util.LinkedHashMap;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.Map;
/**
* A cursor adapter for a conversation thread. Ultimately
@ -46,8 +48,9 @@ import java.util.LinkedHashMap;
public class ConversationAdapter extends CursorAdapter implements AbsListView.RecyclerListener {
private static final int MAX_CACHE_SIZE = 40;
private final Map<String,SoftReference<MessageRecord>> messageRecordCache =
Collections.synchronizedMap(new LRUCache<String, SoftReference<MessageRecord>>(MAX_CACHE_SIZE));
private final LinkedHashMap<String,MessageRecord> messageRecordCache;
private final Handler failedIconClickHandler;
private final Context context;
private final MasterSecret masterSecret;
@ -62,7 +65,6 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
this.masterSecret = masterSecret;
this.failedIconClickHandler = failedIconClickHandler;
this.groupThread = groupThread;
this.messageRecordCache = initializeCache();
this.inflater = (LayoutInflater)context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@ -111,14 +113,22 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
}
private MessageRecord getMessageRecord(long messageId, Cursor cursor, String type) {
if (messageRecordCache.containsKey(type + messageId))
return messageRecordCache.get(type + messageId);
SoftReference<MessageRecord> reference = messageRecordCache.get(type + messageId);
MmsSmsDatabase.Reader reader = DatabaseFactory.getMmsSmsDatabase(context).readerFor(cursor, masterSecret);
if (reference != null) {
MessageRecord record = reference.get();
if (record != null)
return record;
}
MmsSmsDatabase.Reader reader = DatabaseFactory.getMmsSmsDatabase(context)
.readerFor(cursor, masterSecret);
MessageRecord messageRecord = reader.getCurrent();
messageRecordCache.put(type + messageId, messageRecord);
messageRecordCache.put(type + messageId, new SoftReference<MessageRecord>(messageRecord));
return messageRecord;
}
@ -136,13 +146,4 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
public void onMovedToScrapHeap(View view) {
((ConversationItem)view).unbind();
}
private LinkedHashMap<String,MessageRecord> initializeCache() {
return new LinkedHashMap<String,MessageRecord>() {
@Override
protected boolean removeEldestEntry(Entry<String,MessageRecord> eldest) {
return this.size() > MAX_CACHE_SIZE;
}
};
}
}

@ -30,10 +30,12 @@ import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.InvalidMessageException;
import org.thoughtcrime.securesms.util.LRUCache;
import java.lang.ref.SoftReference;
import java.util.LinkedHashMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class EncryptingSmsDatabase extends SmsDatabase {
@ -156,37 +158,25 @@ public class EncryptingSmsDatabase extends SmsDatabase {
private static class PlaintextCache {
private static final int MAX_CACHE_SIZE = 2000;
private static final LinkedHashMap<String,SoftReference<String>> decryptedBodyCache
= new LinkedHashMap<String,SoftReference<String>>()
{
@Override
protected boolean removeEldestEntry(Entry<String,SoftReference<String>> eldest) {
return this.size() > MAX_CACHE_SIZE;
}
};
private static final Map<String, SoftReference<String>> decryptedBodyCache =
Collections.synchronizedMap(new LRUCache<String, SoftReference<String>>(MAX_CACHE_SIZE));
public void put(String ciphertext, String plaintext) {
decryptedBodyCache.put(ciphertext, new SoftReference<String>(plaintext));
}
public String get(String ciphertext) {
synchronized (decryptedBodyCache) {
SoftReference<String> plaintextReference = decryptedBodyCache.get(ciphertext);
if (plaintextReference != null) {
String plaintext = plaintextReference.get();
if (plaintext != null) {
return plaintext;
} else {
decryptedBodyCache.remove(ciphertext);
return null;
}
}
SoftReference<String> plaintextReference = decryptedBodyCache.get(ciphertext);
return null;
if (plaintextReference != null) {
String plaintext = plaintextReference.get();
if (plaintext != null) {
return plaintext;
}
}
return null;
}
}
}

@ -41,12 +41,16 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.InvalidMessageException;
import org.thoughtcrime.securesms.util.LRUCache;
import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.thoughtcrime.securesms.util.Trimmer;
import org.thoughtcrime.securesms.util.Util;
import java.io.UnsupportedEncodingException;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
@ -130,6 +134,8 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
};
public static final ExecutorService slideResolver = Util.newSingleThreadedLifoExecutor();
private static final Map<Long, SoftReference<SlideDeck>> slideCache =
Collections.synchronizedMap(new LRUCache<Long, SoftReference<SlideDeck>>(20));
public MmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
super(context, databaseHelper);
@ -801,6 +807,12 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
private ListenableFutureTask<SlideDeck> getSlideDeck(final MasterSecret masterSecret,
final long id)
{
ListenableFutureTask<SlideDeck> future = getCachedSlideDeck(id);
if (future != null) {
return future;
}
Callable<SlideDeck> task = new Callable<SlideDeck>() {
@Override
public SlideDeck call() throws Exception {
@ -808,16 +820,43 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
return null;
PduBody body = getPartDatabase(masterSecret).getParts(id, false);
return new SlideDeck(context, masterSecret, body);
SlideDeck slideDeck = new SlideDeck(context, masterSecret, body);
slideCache.put(id, new SoftReference<SlideDeck>(slideDeck));
return slideDeck;
}
};
ListenableFutureTask<SlideDeck> future = new ListenableFutureTask<SlideDeck>(task, null);
future = new ListenableFutureTask<SlideDeck>(task, null);
slideResolver.execute(future);
return future;
}
private ListenableFutureTask<SlideDeck> getCachedSlideDeck(final long id) {
SoftReference<SlideDeck> reference = slideCache.get(id);
if (reference != null) {
final SlideDeck slideDeck = reference.get();
if (slideDeck != null) {
Callable<SlideDeck> task = new Callable<SlideDeck>() {
@Override
public SlideDeck call() throws Exception {
return slideDeck;
}
};
ListenableFutureTask<SlideDeck> future = new ListenableFutureTask<SlideDeck>(task, null);
future.run();
return future;
}
}
return null;
}
public void close() {
cursor.close();
}

@ -33,13 +33,15 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.LRUCache;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.LinkedHashMap;
import java.util.Collections;
import java.util.Map;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.pdu.PduPart;
@ -47,13 +49,8 @@ import ws.com.google.android.mms.pdu.PduPart;
public class ImageSlide extends Slide {
private static final int MAX_CACHE_SIZE = 10;
private static final LinkedHashMap<Uri,SoftReference<Bitmap>> thumbnailCache = new LinkedHashMap<Uri,SoftReference<Bitmap>>() {
@Override
protected boolean removeEldestEntry(Entry<Uri,SoftReference<Bitmap>> eldest) {
return this.size() > MAX_CACHE_SIZE;
}
};
private static final Map<Uri, SoftReference<Bitmap>> thumbnailCache =
Collections.synchronizedMap(new LRUCache<Uri, SoftReference<Bitmap>>(MAX_CACHE_SIZE));
public ImageSlide(Context context, MasterSecret masterSecret, PduPart part) {
super(context, masterSecret, part);

@ -16,30 +16,28 @@
*/
package org.thoughtcrime.securesms.mms;
import java.io.UnsupportedEncodingException;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.util.LRUCache;
import java.io.UnsupportedEncodingException;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.Map;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.pdu.CharacterSets;
import ws.com.google.android.mms.pdu.PduPart;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
public class TextSlide extends Slide {
private static final int MAX_CACHE_SIZE = 10;
private static final LinkedHashMap<Uri,String> textCache = new LinkedHashMap<Uri,String>() {
@Override
protected boolean removeEldestEntry(Entry<Uri,String> eldest) {
return this.size() > MAX_CACHE_SIZE;
}
};
private static final Map<Uri, SoftReference<String>> textCache =
Collections.synchronizedMap(new LRUCache<Uri, SoftReference<String>>(MAX_CACHE_SIZE));
public TextSlide(Context context, MasterSecret masterSecret, PduPart part) {
super(context, masterSecret, part);
}
@ -54,16 +52,25 @@ public class TextSlide extends Slide {
}
@Override
public String getText() {
public String getText() {
try {
if (textCache.containsKey(part.getDataUri()))
return textCache.get(part.getDataUri());
SoftReference<String> reference = textCache.get(part.getDataUri());
if (reference != null) {
String cachedText = reference.get();
if (cachedText != null) {
return cachedText;
}
}
String text = new String(getPartData(), CharacterSets.getMimeName(part.getCharset()));
textCache.put(part.getDataUri(), text);
textCache.put(part.getDataUri(), new SoftReference<String>(text));
return text;
} catch (UnsupportedEncodingException uee) {
Log.w("TextSlide", uee);
return new String(getPartData());
}
}
@ -75,7 +82,7 @@ public class TextSlide extends Slide {
part.setData(message.getBytes(CharacterSets.MIMENAME_ISO_8859_1));
if (part.getData().length == 0)
throw new AssertionError("Part data should not be zero!");
throw new AssertionError("Part data should not be zero!");
} catch (UnsupportedEncodingException e) {
Log.w("TextSlide", "ISO_8859_1 must be supported!", e);
@ -89,5 +96,4 @@ public class TextSlide extends Slide {
return part;
}
}

Loading…
Cancel
Save