diff options
| author | Winson Chung <winsonc@google.com> | 2010-07-22 13:54:27 -0700 |
|---|---|---|
| committer | Winson Chung <winsonc@google.com> | 2010-07-22 14:58:19 -0700 |
| commit | c6d6d4a4e73fcb63eaa13d66fcbf26d847799838 (patch) | |
| tree | 1030a0de4695af1a9b385f5c566433c9b7ee5227 /core/java/android/widget/RemoteViewsAdapter.java | |
| parent | 8dac3bf922de433bc7d08c9968dfc5087de34b86 (diff) | |
Fixing indexing issue causing certain items not to load. Performance improvements + testing temporary loading scheme.
Change-Id: I65bfb60237f397ff546b77fbef1b4a7d1c8cba07
Diffstat (limited to 'core/java/android/widget/RemoteViewsAdapter.java')
| -rw-r--r-- | core/java/android/widget/RemoteViewsAdapter.java | 232 |
1 files changed, 126 insertions, 106 deletions
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 07a54ebbf0e7..6282c2df2d51 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -16,8 +16,8 @@ package android.widget; -import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; import android.content.ComponentName; @@ -85,6 +85,10 @@ public class RemoteViewsAdapter extends BaseAdapter { public void onServiceConnected(ComponentName name, IBinder service) { mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service); mConnected = true; + + // start the background loader + mViewCache.startBackgroundLoader(); + // notifyDataSetChanged should be called first, to ensure that the // views are not updated twice notifyDataSetChanged(); @@ -112,12 +116,15 @@ public class RemoteViewsAdapter extends BaseAdapter { public void onServiceDisconnected(ComponentName name) { mRemoteViewsFactory = null; mConnected = false; - if (mCallback != null) - mCallback.onRemoteAdapterDisconnected(); // clear the main/worker queues mMainQueue.removeMessages(0); - mWorkerQueue.removeMessages(0); + + // stop the background loader + mViewCache.stopBackgroundLoader(); + + if (mCallback != null) + mCallback.onRemoteAdapterDisconnected(); } public IRemoteViewsFactory getRemoteViewsFactory() { @@ -135,6 +142,9 @@ public class RemoteViewsAdapter extends BaseAdapter { private class RemoteViewsCache { private RemoteViewsInfo mViewCacheInfo; private RemoteViewsIndexInfo[] mViewCache; + private int[] mTmpViewCacheLoadIndices; + private LinkedList<Integer> mViewCacheLoadIndices; + private boolean mBackgroundLoaderEnabled; // if a user loading view is not provided, then we create a temporary one // for the user using the height of the first view @@ -150,16 +160,6 @@ public class RemoteViewsAdapter extends BaseAdapter { private int mCacheSlack; private final float mCacheSlackPercentage = 0.75f; - // determines whether to reorder the posted items on the worker thread - // so that the items in the current window can be loaded first - private int mPriorityLoadingWindowSize; - private int mPriorityLoadingWindowStart; - private int mPriorityLoadingWindowEnd; - - // determines which way to load items in the current window based on how - // the window shifted last - private boolean mLoadUpwards; - /** * The data structure stored at each index of the cache. Any member * that is not invalidated persists throughout the lifetime of the cache. @@ -218,22 +218,21 @@ public class RemoteViewsAdapter extends BaseAdapter { mCacheSlack = Math.round(mCacheSlackPercentage * mHalfCacheSize); mViewCacheStartPosition = 0; mViewCacheEndPosition = -1; - mPriorityLoadingWindowSize = 4; - mPriorityLoadingWindowStart = 0; - mPriorityLoadingWindowEnd = 0; - mLoadUpwards = false; + mBackgroundLoaderEnabled = false; // initialize the cache + int cacheSize = 2 * mHalfCacheSize + 1; mViewCacheInfo = new RemoteViewsInfo(); - mViewCache = new RemoteViewsIndexInfo[2 * mHalfCacheSize + 1]; + mViewCache = new RemoteViewsIndexInfo[cacheSize]; for (int i = 0; i < mViewCache.length; ++i) { mViewCache[i] = new RemoteViewsIndexInfo(); } + mTmpViewCacheLoadIndices = new int[cacheSize]; + mViewCacheLoadIndices = new LinkedList<Integer>(); } private final boolean contains(int position) { - // take the modulo of the position - return (mViewCacheStartPosition <= position) && (position < mViewCacheEndPosition); + return (mViewCacheStartPosition <= position) && (position <= mViewCacheEndPosition); } private final boolean containsAndIsValid(int position) { @@ -247,6 +246,7 @@ public class RemoteViewsAdapter extends BaseAdapter { } private final int getCacheIndex(int position) { + // take the modulo of the position return (mViewCache.length + (position % mViewCache.length)) % mViewCache.length; } @@ -298,7 +298,7 @@ public class RemoteViewsAdapter extends BaseAdapter { synchronized (mViewCache) { // skip if the window has moved - if (position < mViewCacheStartPosition || position >= mViewCacheEndPosition) + if (position < mViewCacheStartPosition || position > mViewCacheEndPosition) return; final int positionIndex = position; @@ -316,16 +316,28 @@ public class RemoteViewsAdapter extends BaseAdapter { RemoteViewsIndexInfo indexInfo = mViewCache[cacheIndex]; FrameLayout flipper = indexInfo.flipper; - // recompose the flipper - View loadingView = flipper.getChildAt(0); - loadingView.setVisibility(View.GONE); - flipper.removeAllViews(); - flipper.addView(loadingView); - flipper.addView(indexInfo.view.apply(mContext, flipper)); - - // hide the loader view and bring the new view to the front - flipper.requestLayout(); - flipper.invalidate(); + // update the flipper + flipper.getChildAt(0).setVisibility(View.GONE); + boolean addNewView = true; + if (flipper.getChildCount() > 1) { + View v = flipper.getChildAt(1); + int typeId = ((Integer) v.getTag()).intValue(); + if (typeId == indexInfo.typeId) { + // we can reapply since it is the same type + indexInfo.view.reapply(mContext, v); + v.setVisibility(View.VISIBLE); + if (v.getAnimation() != null) + v.buildDrawingCache(); + addNewView = false; + } else { + flipper.removeViewAt(1); + } + } + if (addNewView) { + View v = indexInfo.view.apply(mContext, flipper); + v.setTag(new Integer(indexInfo.typeId)); + flipper.addView(v); + } } } } @@ -336,88 +348,52 @@ public class RemoteViewsAdapter extends BaseAdapter { private RemoteViewsIndexInfo requestCachedIndexInfo(final int position) { int indicesToLoadCount = 0; - int[] indicesToLoad = null; synchronized (mViewCache) { - indicesToLoad = new int[mViewCache.length]; - Arrays.fill(indicesToLoad, 0); - if (containsAndIsValid(position)) { // return the info if it exists in the window and is loaded return mViewCache[getCacheIndex(position)]; - } + } // if necessary update the window and load the new information int centerPosition = (mViewCacheEndPosition + mViewCacheStartPosition) / 2; if ((mViewCacheEndPosition <= mViewCacheStartPosition) || (Math.abs(position - centerPosition) > mCacheSlack)) { int newStartPosition = position - mHalfCacheSize; int newEndPosition = position + mHalfCacheSize; + int frameSize = mHalfCacheSize / 4; + int frameCount = (int) Math.ceil(mViewCache.length / (float) frameSize); // prune/add before the current start position int effectiveStart = Math.max(newStartPosition, 0); - int effectiveEnd = Math.min(newEndPosition, getCount()); - - mWorkerQueue.removeMessages(0); + int effectiveEnd = Math.min(newEndPosition, getCount() - 1); // invalidate items in the queue - boolean loadFromBeginning = effectiveStart < mViewCacheStartPosition; - int numLoadFromBeginning = mViewCacheStartPosition - effectiveStart; - boolean loadFromEnd = effectiveEnd > mViewCacheEndPosition; int overlapStart = Math.max(mViewCacheStartPosition, effectiveStart); int overlapEnd = Math.min(Math.max(mViewCacheStartPosition, mViewCacheEndPosition), effectiveEnd); - for (int i = newStartPosition; i < newEndPosition; ++i) { - if (loadFromBeginning && (effectiveStart <= i) && (i < overlapStart)) { - // load new items at the beginning in reverse order - mViewCache[getCacheIndex(i)].invalidate(); - indicesToLoad[indicesToLoadCount++] = effectiveStart - + (numLoadFromBeginning - (i - effectiveStart) - 1); - } else if (loadFromEnd && (overlapEnd <= i) && (i < effectiveEnd)) { - mViewCache[getCacheIndex(i)].invalidate(); - indicesToLoad[indicesToLoadCount++] = i; - } else if ((overlapStart <= i) && (i < overlapEnd)) { - // load the stuff in the middle that has not already - // been loaded - if (!mViewCache[getCacheIndex(i)].isValid()) { - indicesToLoad[indicesToLoadCount++] = i; + for (int i = 0; i < (frameSize * frameCount); ++i) { + int index = newStartPosition + ((i % frameSize) * frameCount + (i / frameSize)); + + if (index <= newEndPosition) { + if ((overlapStart <= index) && (index <= overlapEnd)) { + // load the stuff in the middle that has not already + // been loaded + if (!mViewCache[getCacheIndex(index)].isValid()) { + mTmpViewCacheLoadIndices[indicesToLoadCount++] = index; + } + } else if ((effectiveStart <= index) && (index <= effectiveEnd)) { + // invalidate and load all new effective items + mViewCache[getCacheIndex(index)].invalidate(); + mTmpViewCacheLoadIndices[indicesToLoadCount++] = index; + } else { + // invalidate all other cache indices (outside the effective start/end) + // but don't load + mViewCache[getCacheIndex(index)].invalidate(); } - } else { - // invalidate all other cache indices (outside the effective start/end) - mViewCache[getCacheIndex(i)].invalidate(); } } mViewCacheStartPosition = newStartPosition; mViewCacheEndPosition = newEndPosition; - mPriorityLoadingWindowStart = position; - mPriorityLoadingWindowEnd = position + mPriorityLoadingWindowSize; - mLoadUpwards = loadFromBeginning && !loadFromEnd; - } else if (contains(position)) { - // prioritize items around this position so that they load first - if (position < mPriorityLoadingWindowStart || position > mPriorityLoadingWindowEnd) { - mWorkerQueue.removeMessages(0); - - int index; - int effectiveStart = Math.max(position - mPriorityLoadingWindowSize, 0); - int effectiveEnd = 0; - synchronized (mViewCacheInfo) { - effectiveEnd = Math.min(position + mPriorityLoadingWindowSize - 1, - mViewCacheInfo.count - 1); - } - - for (int i = 0; i < mViewCache.length; ++i) { - if (mLoadUpwards) { - index = effectiveEnd - i; - } else { - index = effectiveStart + i; - } - if (!mViewCache[getCacheIndex(index)].isValid()) { - indicesToLoad[indicesToLoadCount++] = index; - } - } - - mPriorityLoadingWindowStart = effectiveStart; - mPriorityLoadingWindowEnd = position + mPriorityLoadingWindowSize; - } } } @@ -426,15 +402,15 @@ public class RemoteViewsAdapter extends BaseAdapter { synchronized (mViewCacheInfo) { length = mViewCacheInfo.count; } - for (int i = 0; i < indicesToLoadCount; ++i) { - final int index = indicesToLoad[i]; - if (0 <= index && index < length) { - mWorkerQueue.post(new Runnable() { - @Override - public void run() { - updateRemoteViewsInfo(index); + if (indicesToLoadCount > 0) { + synchronized (mViewCacheLoadIndices) { + mViewCacheLoadIndices.clear(); + for (int i = 0; i < indicesToLoadCount; ++i) { + final int index = mTmpViewCacheLoadIndices[i]; + if (0 <= index && index < length) { + mViewCacheLoadIndices.addLast(index); } - }); + } } } @@ -446,7 +422,7 @@ public class RemoteViewsAdapter extends BaseAdapter { if (mServiceConnection.isConnected()) { // create the flipper views if necessary (we have to do this now // for all the flippers while we have the reference to the parent) - createInitialLoadingFlipperViews(parent); + initializeLoadingViews(parent); // request the item from the cache (queueing it to load if not // in the cache already) @@ -456,6 +432,7 @@ public class RemoteViewsAdapter extends BaseAdapter { synchronized (mViewCache) { int cacheIndex = getCacheIndex(position); FrameLayout flipper = mViewCache[cacheIndex].flipper; + flipper.setVisibility(View.VISIBLE); if (indexInfo == null) { // hide the item view and show the loading view @@ -463,17 +440,12 @@ public class RemoteViewsAdapter extends BaseAdapter { for (int i = 1; i < flipper.getChildCount(); ++i) { flipper.getChildAt(i).setVisibility(View.GONE); } - flipper.requestLayout(); - flipper.invalidate(); } else { // hide the loading view and show the item view - flipper.setVisibility(View.VISIBLE); for (int i = 0; i < flipper.getChildCount() - 1; ++i) { flipper.getChildAt(i).setVisibility(View.GONE); } flipper.getChildAt(flipper.getChildCount() - 1).setVisibility(View.VISIBLE); - flipper.requestLayout(); - flipper.invalidate(); } return flipper; } @@ -481,7 +453,7 @@ public class RemoteViewsAdapter extends BaseAdapter { return new View(mContext); } - private void createInitialLoadingFlipperViews(ViewGroup parent) { + private void initializeLoadingViews(ViewGroup parent) { // ensure that the cache has the appropriate initial flipper synchronized (mViewCache) { if (mViewCache[0].flipper == null) { @@ -519,6 +491,50 @@ public class RemoteViewsAdapter extends BaseAdapter { } } + public void startBackgroundLoader() { + // initialize the worker runnable + mBackgroundLoaderEnabled = true; + mWorkerQueue.post(new Runnable() { + @Override + public void run() { + while (mBackgroundLoaderEnabled) { + int index = -1; + synchronized (mViewCacheLoadIndices) { + if (!mViewCacheLoadIndices.isEmpty()) { + index = mViewCacheLoadIndices.removeFirst(); + } + } + if (index < 0) { + // there were no items to load, so sleep for a bit + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else { + // otherwise, try and load the item + updateRemoteViewsInfo(index); + + // sleep for a bit to allow things to catch up after the load + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + }); + } + + public void stopBackgroundLoader() { + // clear the items to be loaded + mBackgroundLoaderEnabled = false; + synchronized (mViewCacheLoadIndices) { + mViewCacheLoadIndices.clear(); + } + } + public long getItemId(int position) { synchronized (mViewCache) { if (containsAndIsValid(position)) { @@ -567,9 +583,13 @@ public class RemoteViewsAdapter extends BaseAdapter { } public void flushCache() { + // clear the items to be loaded + synchronized (mViewCacheLoadIndices) { + mViewCacheLoadIndices.clear(); + } + synchronized (mViewCache) { // flush the internal cache and invalidate the adapter for future loads - mWorkerQueue.removeMessages(0); mMainQueue.removeMessages(0); for (int i = 0; i < mViewCache.length; ++i) { |
