Making requested indices the last to be pruned when we hit cache memory limits. (3368863)

- Also tweaking disconnect delay, cache size, and memory limit

Change-Id: If67188dcb363e5a2fbc02736f82bfd783af02533
This commit is contained in:
Winson Chung
2011-01-26 13:36:34 -08:00
parent 63b38e31aa
commit b90a91c633

View File

@@ -48,11 +48,11 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
private static final String TAG = "RemoteViewsAdapter"; private static final String TAG = "RemoteViewsAdapter";
// The max number of items in the cache // The max number of items in the cache
private static final int sDefaultCacheSize = 50; private static final int sDefaultCacheSize = 40;
// The delay (in millis) to wait until attempting to unbind from a service after a request. // The delay (in millis) to wait until attempting to unbind from a service after a request.
// This ensures that we don't stay continually bound to the service and that it can be destroyed // This ensures that we don't stay continually bound to the service and that it can be destroyed
// if we need the memory elsewhere in the system. // if we need the memory elsewhere in the system.
private static final int sUnbindServiceDelay = 1000; private static final int sUnbindServiceDelay = 5000;
// Type defs for controlling different messages across the main and worker message queues // Type defs for controlling different messages across the main and worker message queues
private static final int sDefaultMessageType = 0; private static final int sDefaultMessageType = 0;
private static final int sUnbindServiceMessageType = 1; private static final int sUnbindServiceMessageType = 1;
@@ -421,17 +421,19 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
private class RemoteViewsIndexMetaData { private class RemoteViewsIndexMetaData {
int typeId; int typeId;
long itemId; long itemId;
boolean isRequested;
public RemoteViewsIndexMetaData(RemoteViews v, long itemId) { public RemoteViewsIndexMetaData(RemoteViews v, long itemId, boolean requested) {
set(v, itemId); set(v, itemId, requested);
} }
public void set(RemoteViews v, long id) { public void set(RemoteViews v, long id, boolean requested) {
itemId = id; itemId = id;
if (v != null) if (v != null)
typeId = v.getLayoutId(); typeId = v.getLayoutId();
else else
typeId = 0; typeId = 0;
isRequested = requested;
} }
} }
@@ -461,6 +463,10 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
// The set of indices that have been explicitly requested by the collection view // The set of indices that have been explicitly requested by the collection view
private HashSet<Integer> mRequestedIndices; private HashSet<Integer> mRequestedIndices;
// We keep a reference of the last requested index to determine which item to prune the
// farthest items from when we hit the memory limit
private int mLastRequestedIndex;
// The set of indices to load, including those explicitly requested, as well as those // The set of indices to load, including those explicitly requested, as well as those
// determined by the preloading algorithm to be prefetched // determined by the preloading algorithm to be prefetched
private HashSet<Integer> mLoadIndices; private HashSet<Integer> mLoadIndices;
@@ -477,7 +483,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
private int mMaxCount; private int mMaxCount;
private int mMaxCountSlack; private int mMaxCountSlack;
private static final float sMaxCountSlackPercent = 0.75f; private static final float sMaxCountSlackPercent = 0.75f;
private static final int sMaxMemoryUsage = 1024 * 1024; private static final int sMaxMemoryLimitInBytes = 2 * 1024 * 1024;
public FixedSizeRemoteViewsCache(int maxCacheSize) { public FixedSizeRemoteViewsCache(int maxCacheSize) {
mMaxCount = maxCacheSize; mMaxCount = maxCacheSize;
@@ -489,31 +495,33 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>(); mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>();
mIndexRemoteViews = new HashMap<Integer, RemoteViews>(); mIndexRemoteViews = new HashMap<Integer, RemoteViews>();
mRequestedIndices = new HashSet<Integer>(); mRequestedIndices = new HashSet<Integer>();
mLastRequestedIndex = -1;
mLoadIndices = new HashSet<Integer>(); mLoadIndices = new HashSet<Integer>();
} }
public void insert(int position, RemoteViews v, long itemId) { public void insert(int position, RemoteViews v, long itemId, boolean isRequested) {
// Trim the cache if we go beyond the count // Trim the cache if we go beyond the count
if (mIndexRemoteViews.size() >= mMaxCount) { if (mIndexRemoteViews.size() >= mMaxCount) {
mIndexRemoteViews.remove(getFarthestPositionFrom(position)); mIndexRemoteViews.remove(getFarthestPositionFrom(position));
} }
// Trim the cache if we go beyond the available memory size constraints // Trim the cache if we go beyond the available memory size constraints
while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryUsage) { int pruneFromPosition = (mLastRequestedIndex > -1) ? mLastRequestedIndex : position;
while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryLimitInBytes) {
// Note: This is currently the most naive mechanism for deciding what to prune when // Note: This is currently the most naive mechanism for deciding what to prune when
// we hit the memory limit. In the future, we may want to calculate which index to // we hit the memory limit. In the future, we may want to calculate which index to
// remove based on both its position as well as it's current memory usage, as well // remove based on both its position as well as it's current memory usage, as well
// as whether it was directly requested vs. whether it was preloaded by our caching // as whether it was directly requested vs. whether it was preloaded by our caching
// mechanism. // mechanism.
mIndexRemoteViews.remove(getFarthestPositionFrom(position)); mIndexRemoteViews.remove(getFarthestPositionFrom(pruneFromPosition));
} }
// Update the metadata cache // Update the metadata cache
if (mIndexMetaData.containsKey(position)) { if (mIndexMetaData.containsKey(position)) {
final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position); final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position);
metaData.set(v, itemId); metaData.set(v, itemId, isRequested);
} else { } else {
mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId)); mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId, isRequested));
} }
mIndexRemoteViews.put(position, v); mIndexRemoteViews.put(position, v);
} }
@@ -560,17 +568,31 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
// Find the index farthest away and remove that // Find the index farthest away and remove that
int maxDist = 0; int maxDist = 0;
int maxDistIndex = -1; int maxDistIndex = -1;
int maxDistNonRequested = 0;
int maxDistIndexNonRequested = -1;
for (int i : mIndexRemoteViews.keySet()) { for (int i : mIndexRemoteViews.keySet()) {
int dist = Math.abs(i-pos); int dist = Math.abs(i-pos);
if (dist > maxDistNonRequested && !mIndexMetaData.get(i).isRequested) {
// maxDistNonRequested/maxDistIndexNonRequested will store the index of the
// farthest non-requested position
maxDistIndexNonRequested = i;
maxDistNonRequested = dist;
}
if (dist > maxDist) { if (dist > maxDist) {
// maxDist/maxDistIndex will store the index of the farthest position
// regardless of whether it was directly requested or not
maxDistIndex = i; maxDistIndex = i;
maxDist = dist; maxDist = dist;
} }
} }
if (maxDistIndexNonRequested > -1) {
return maxDistIndexNonRequested;
}
return maxDistIndex; return maxDistIndex;
} }
public void queueRequestedPositionToLoad(int position) { public void queueRequestedPositionToLoad(int position) {
mLastRequestedIndex = position;
synchronized (mLoadIndices) { synchronized (mLoadIndices) {
mRequestedIndices.add(position); mRequestedIndices.add(position);
mLoadIndices.add(position); mLoadIndices.add(position);
@@ -610,7 +632,8 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
} }
return true; return true;
} }
public int getNextIndexToLoad() { /** Returns the next index to load, and whether that index was directly requested or not */
public int[] getNextIndexToLoad() {
// We try and prioritize items that have been requested directly, instead // We try and prioritize items that have been requested directly, instead
// of items that are loaded as a result of the caching mechanism // of items that are loaded as a result of the caching mechanism
synchronized (mLoadIndices) { synchronized (mLoadIndices) {
@@ -619,17 +642,17 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
Integer i = mRequestedIndices.iterator().next(); Integer i = mRequestedIndices.iterator().next();
mRequestedIndices.remove(i); mRequestedIndices.remove(i);
mLoadIndices.remove(i); mLoadIndices.remove(i);
return i.intValue(); return new int[]{i.intValue(), 1};
} }
// Otherwise, preload other indices as necessary // Otherwise, preload other indices as necessary
if (!mLoadIndices.isEmpty()) { if (!mLoadIndices.isEmpty()) {
Integer i = mLoadIndices.iterator().next(); Integer i = mLoadIndices.iterator().next();
mLoadIndices.remove(i); mLoadIndices.remove(i);
return i.intValue(); return new int[]{i.intValue(), 0};
} }
return -1; return new int[]{-1, 0};
} }
} }
@@ -647,6 +670,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
mPreloadLowerBound = 0; mPreloadLowerBound = 0;
mPreloadUpperBound = -1; mPreloadUpperBound = -1;
mLastRequestedIndex = -1;
mIndexRemoteViews.clear(); mIndexRemoteViews.clear();
mIndexMetaData.clear(); mIndexMetaData.clear();
synchronized (mLoadIndices) { synchronized (mLoadIndices) {
@@ -691,12 +715,15 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
if (mServiceConnection.isConnected()) { if (mServiceConnection.isConnected()) {
// Get the next index to load // Get the next index to load
int position = -1; int position = -1;
boolean isRequested = false;
synchronized (mCache) { synchronized (mCache) {
position = mCache.getNextIndexToLoad(); int[] res = mCache.getNextIndexToLoad();
position = res[0];
isRequested = res[1] > 0;
} }
if (position > -1) { if (position > -1) {
// Load the item, and notify any existing RemoteViewsFrameLayouts // Load the item, and notify any existing RemoteViewsFrameLayouts
updateRemoteViews(position); updateRemoteViews(position, isRequested);
// Queue up for the next one to load // Queue up for the next one to load
loadNextIndexInBackground(); loadNextIndexInBackground();
@@ -756,7 +783,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
} }
} }
private void updateRemoteViews(final int position) { private void updateRemoteViews(final int position, boolean isRequested) {
if (!mServiceConnection.isConnected()) return; if (!mServiceConnection.isConnected()) return;
IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
@@ -784,7 +811,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
} }
synchronized (mCache) { synchronized (mCache) {
// Cache the RemoteViews we loaded // Cache the RemoteViews we loaded
mCache.insert(position, remoteViews, itemId); mCache.insert(position, remoteViews, itemId, isRequested);
// Notify all the views that we have previously returned for this index that // Notify all the views that we have previously returned for this index that
// there is new data for it. // there is new data for it.