Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d98a803b authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "Fixing indexing issue causing certain items not to load. Performance...

Merge "Fixing indexing issue causing certain items not to load.  Performance improvements + testing temporary loading scheme."
parents b621677c c6d6d4a4
Loading
Loading
Loading
Loading
+126 −106
Original line number Original line Diff line number Diff line
@@ -16,8 +16,8 @@


package android.widget;
package android.widget;


import java.util.Arrays;
import java.util.HashMap;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map;


import android.content.ComponentName;
import android.content.ComponentName;
@@ -85,6 +85,10 @@ public class RemoteViewsAdapter extends BaseAdapter {
        public void onServiceConnected(ComponentName name, IBinder service) {
        public void onServiceConnected(ComponentName name, IBinder service) {
            mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
            mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
            mConnected = true;
            mConnected = true;

            // start the background loader
            mViewCache.startBackgroundLoader();

            // notifyDataSetChanged should be called first, to ensure that the
            // notifyDataSetChanged should be called first, to ensure that the
            // views are not updated twice
            // views are not updated twice
            notifyDataSetChanged();
            notifyDataSetChanged();
@@ -112,12 +116,15 @@ public class RemoteViewsAdapter extends BaseAdapter {
        public void onServiceDisconnected(ComponentName name) {
        public void onServiceDisconnected(ComponentName name) {
            mRemoteViewsFactory = null;
            mRemoteViewsFactory = null;
            mConnected = false;
            mConnected = false;
            if (mCallback != null)
                mCallback.onRemoteAdapterDisconnected();


            // clear the main/worker queues
            // clear the main/worker queues
            mMainQueue.removeMessages(0);
            mMainQueue.removeMessages(0);
            mWorkerQueue.removeMessages(0);
            
            // stop the background loader
            mViewCache.stopBackgroundLoader();

            if (mCallback != null)
                mCallback.onRemoteAdapterDisconnected();
        }
        }


        public IRemoteViewsFactory getRemoteViewsFactory() {
        public IRemoteViewsFactory getRemoteViewsFactory() {
@@ -135,6 +142,9 @@ public class RemoteViewsAdapter extends BaseAdapter {
    private class RemoteViewsCache {
    private class RemoteViewsCache {
        private RemoteViewsInfo mViewCacheInfo;
        private RemoteViewsInfo mViewCacheInfo;
        private RemoteViewsIndexInfo[] mViewCache;
        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
        // if a user loading view is not provided, then we create a temporary one
        // for the user using the height of the first view
        // for the user using the height of the first view
@@ -150,16 +160,6 @@ public class RemoteViewsAdapter extends BaseAdapter {
        private int mCacheSlack;
        private int mCacheSlack;
        private final float mCacheSlackPercentage = 0.75f;
        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 
         * The data structure stored at each index of the cache. Any member 
         * that is not invalidated persists throughout the lifetime of the cache.
         * 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);
            mCacheSlack = Math.round(mCacheSlackPercentage * mHalfCacheSize);
            mViewCacheStartPosition = 0;
            mViewCacheStartPosition = 0;
            mViewCacheEndPosition = -1;
            mViewCacheEndPosition = -1;
            mPriorityLoadingWindowSize = 4;
            mBackgroundLoaderEnabled = false;
            mPriorityLoadingWindowStart = 0;
            mPriorityLoadingWindowEnd = 0;
            mLoadUpwards = false;


            // initialize the cache
            // initialize the cache
            int cacheSize = 2 * mHalfCacheSize + 1;
            mViewCacheInfo = new RemoteViewsInfo();
            mViewCacheInfo = new RemoteViewsInfo();
            mViewCache = new RemoteViewsIndexInfo[2 * mHalfCacheSize + 1];
            mViewCache = new RemoteViewsIndexInfo[cacheSize];
            for (int i = 0; i < mViewCache.length; ++i) {
            for (int i = 0; i < mViewCache.length; ++i) {
                mViewCache[i] = new RemoteViewsIndexInfo();
                mViewCache[i] = new RemoteViewsIndexInfo();
            }
            }
            mTmpViewCacheLoadIndices = new int[cacheSize];
            mViewCacheLoadIndices = new LinkedList<Integer>();
        }
        }


        private final boolean contains(int position) {
        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) {
        private final boolean containsAndIsValid(int position) {
@@ -247,6 +246,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
        }
        }


        private final int getCacheIndex(int position) {
        private final int getCacheIndex(int position) {
            // take the modulo of the position
            return (mViewCache.length + (position % mViewCache.length)) % mViewCache.length;
            return (mViewCache.length + (position % mViewCache.length)) % mViewCache.length;
        }
        }


@@ -298,7 +298,7 @@ public class RemoteViewsAdapter extends BaseAdapter {


                synchronized (mViewCache) {
                synchronized (mViewCache) {
                    // skip if the window has moved
                    // skip if the window has moved
                    if (position < mViewCacheStartPosition || position >= mViewCacheEndPosition)
                    if (position < mViewCacheStartPosition || position > mViewCacheEndPosition)
                        return;
                        return;


                    final int positionIndex = position;
                    final int positionIndex = position;
@@ -316,16 +316,28 @@ public class RemoteViewsAdapter extends BaseAdapter {
                                    RemoteViewsIndexInfo indexInfo = mViewCache[cacheIndex];
                                    RemoteViewsIndexInfo indexInfo = mViewCache[cacheIndex];
                                    FrameLayout flipper = indexInfo.flipper;
                                    FrameLayout flipper = indexInfo.flipper;


                                    // recompose the flipper
                                    // update the flipper
                                    View loadingView = flipper.getChildAt(0);
                                    flipper.getChildAt(0).setVisibility(View.GONE);
                                    loadingView.setVisibility(View.GONE);
                                    boolean addNewView = true;
                                    flipper.removeAllViews();
                                    if (flipper.getChildCount() > 1) {
                                    flipper.addView(loadingView);
                                        View v = flipper.getChildAt(1);
                                    flipper.addView(indexInfo.view.apply(mContext, flipper));
                                        int typeId = ((Integer) v.getTag()).intValue();

                                        if (typeId == indexInfo.typeId) {
                                    // hide the loader view and bring the new view to the front
                                            // we can reapply since it is the same type
                                    flipper.requestLayout();
                                            indexInfo.view.reapply(mContext, v);
                                    flipper.invalidate();
                                            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,12 +348,8 @@ public class RemoteViewsAdapter extends BaseAdapter {


        private RemoteViewsIndexInfo requestCachedIndexInfo(final int position) {
        private RemoteViewsIndexInfo requestCachedIndexInfo(final int position) {
            int indicesToLoadCount = 0;
            int indicesToLoadCount = 0;
            int[] indicesToLoad = null;


            synchronized (mViewCache) {
            synchronized (mViewCache) {
                indicesToLoad = new int[mViewCache.length];
                Arrays.fill(indicesToLoad, 0);

                if (containsAndIsValid(position)) {
                if (containsAndIsValid(position)) {
                    // return the info if it exists in the window and is loaded
                    // return the info if it exists in the window and is loaded
                    return mViewCache[getCacheIndex(position)];
                    return mViewCache[getCacheIndex(position)];
@@ -352,72 +360,40 @@ public class RemoteViewsAdapter extends BaseAdapter {
                if ((mViewCacheEndPosition <= mViewCacheStartPosition) || (Math.abs(position - centerPosition) > mCacheSlack)) {
                if ((mViewCacheEndPosition <= mViewCacheStartPosition) || (Math.abs(position - centerPosition) > mCacheSlack)) {
                    int newStartPosition = position - mHalfCacheSize;
                    int newStartPosition = position - mHalfCacheSize;
                    int newEndPosition = 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
                    // prune/add before the current start position
                    int effectiveStart = Math.max(newStartPosition, 0);
                    int effectiveStart = Math.max(newStartPosition, 0);
                    int effectiveEnd = Math.min(newEndPosition, getCount());
                    int effectiveEnd = Math.min(newEndPosition, getCount() - 1);

                    mWorkerQueue.removeMessages(0);


                    // invalidate items in the queue
                    // invalidate items in the queue
                    boolean loadFromBeginning = effectiveStart < mViewCacheStartPosition;
                    int numLoadFromBeginning = mViewCacheStartPosition - effectiveStart;
                    boolean loadFromEnd = effectiveEnd > mViewCacheEndPosition;
                    int overlapStart = Math.max(mViewCacheStartPosition, effectiveStart);
                    int overlapStart = Math.max(mViewCacheStartPosition, effectiveStart);
                    int overlapEnd = Math.min(Math.max(mViewCacheStartPosition, mViewCacheEndPosition), effectiveEnd);
                    int overlapEnd = Math.min(Math.max(mViewCacheStartPosition, mViewCacheEndPosition), effectiveEnd);
                    for (int i = newStartPosition; i < newEndPosition; ++i) {
                    for (int i = 0; i < (frameSize * frameCount); ++i) {
                        if (loadFromBeginning && (effectiveStart <= i) && (i < overlapStart)) {
                        int index = newStartPosition + ((i % frameSize) * frameCount + (i / frameSize));
                            // load new items at the beginning in reverse order
                        
                            mViewCache[getCacheIndex(i)].invalidate();
                        if (index <= newEndPosition) {
                            indicesToLoad[indicesToLoadCount++] = effectiveStart
                            if ((overlapStart <= index) && (index <= overlapEnd)) {
                                    + (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
                                // load the stuff in the middle that has not already
                                // been loaded
                                // been loaded
                            if (!mViewCache[getCacheIndex(i)].isValid()) {
                                if (!mViewCache[getCacheIndex(index)].isValid()) {
                                indicesToLoad[indicesToLoadCount++] = i;
                                    mTmpViewCacheLoadIndices[indicesToLoadCount++] = index;
                                }
                                }
                            } else if ((effectiveStart <= index) && (index <= effectiveEnd)) {
                                // invalidate and load all new effective items
                                mViewCache[getCacheIndex(index)].invalidate();
                                mTmpViewCacheLoadIndices[indicesToLoadCount++] = index;
                            } else {
                            } else {
                                // invalidate all other cache indices (outside the effective start/end)
                                // invalidate all other cache indices (outside the effective start/end)
                            mViewCache[getCacheIndex(i)].invalidate();
                                // but don't load
                                mViewCache[getCacheIndex(index)].invalidate();
                            }
                        }
                        }
                    }
                    }


                    mViewCacheStartPosition = newStartPosition;
                    mViewCacheStartPosition = newStartPosition;
                    mViewCacheEndPosition = newEndPosition;
                    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) {
            synchronized (mViewCacheInfo) {
                length = mViewCacheInfo.count;
                length = mViewCacheInfo.count;
            }
            }
            if (indicesToLoadCount > 0) {
                synchronized (mViewCacheLoadIndices) {
                    mViewCacheLoadIndices.clear();
                    for (int i = 0; i < indicesToLoadCount; ++i) {
                    for (int i = 0; i < indicesToLoadCount; ++i) {
                final int index = indicesToLoad[i];
                        final int index = mTmpViewCacheLoadIndices[i];
                        if (0 <= index && index < length) {
                        if (0 <= index && index < length) {
                    mWorkerQueue.post(new Runnable() {
                            mViewCacheLoadIndices.addLast(index);
                        @Override
                        }
                        public void run() {
                            updateRemoteViewsInfo(index);
                    }
                    }
                    });
                }
                }
            }
            }


@@ -446,7 +422,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
            if (mServiceConnection.isConnected()) {
            if (mServiceConnection.isConnected()) {
                // create the flipper views if necessary (we have to do this now
                // create the flipper views if necessary (we have to do this now
                // for all the flippers while we have the reference to the parent)
                // 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
                // request the item from the cache (queueing it to load if not
                // in the cache already)
                // in the cache already)
@@ -456,6 +432,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
                synchronized (mViewCache) {
                synchronized (mViewCache) {
                    int cacheIndex = getCacheIndex(position);
                    int cacheIndex = getCacheIndex(position);
                    FrameLayout flipper = mViewCache[cacheIndex].flipper;
                    FrameLayout flipper = mViewCache[cacheIndex].flipper;
                    flipper.setVisibility(View.VISIBLE);


                    if (indexInfo == null) {
                    if (indexInfo == null) {
                        // hide the item view and show the loading view
                        // 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) {
                        for (int i = 1; i < flipper.getChildCount(); ++i) {
                            flipper.getChildAt(i).setVisibility(View.GONE);
                            flipper.getChildAt(i).setVisibility(View.GONE);
                        }
                        }
                        flipper.requestLayout();
                        flipper.invalidate();
                    } else {
                    } else {
                        // hide the loading view and show the item view
                        // hide the loading view and show the item view
                        flipper.setVisibility(View.VISIBLE);
                        for (int i = 0; i < flipper.getChildCount() - 1; ++i) {
                        for (int i = 0; i < flipper.getChildCount() - 1; ++i) {
                            flipper.getChildAt(i).setVisibility(View.GONE);
                            flipper.getChildAt(i).setVisibility(View.GONE);
                        }
                        }
                        flipper.getChildAt(flipper.getChildCount() - 1).setVisibility(View.VISIBLE);
                        flipper.getChildAt(flipper.getChildCount() - 1).setVisibility(View.VISIBLE);
                        flipper.requestLayout();
                        flipper.invalidate();
                    }
                    }
                    return flipper;
                    return flipper;
                }
                }
@@ -481,7 +453,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
            return new View(mContext);
            return new View(mContext);
        }
        }


        private void createInitialLoadingFlipperViews(ViewGroup parent) {
        private void initializeLoadingViews(ViewGroup parent) {
            // ensure that the cache has the appropriate initial flipper
            // ensure that the cache has the appropriate initial flipper
            synchronized (mViewCache) {
            synchronized (mViewCache) {
                if (mViewCache[0].flipper == null) {
                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) {
        public long getItemId(int position) {
            synchronized (mViewCache) {
            synchronized (mViewCache) {
                if (containsAndIsValid(position)) {
                if (containsAndIsValid(position)) {
@@ -567,9 +583,13 @@ public class RemoteViewsAdapter extends BaseAdapter {
        }
        }


        public void flushCache() {
        public void flushCache() {
            // clear the items to be loaded
            synchronized (mViewCacheLoadIndices) {
                mViewCacheLoadIndices.clear();
            }

            synchronized (mViewCache) {
            synchronized (mViewCache) {
                // flush the internal cache and invalidate the adapter for future loads
                // flush the internal cache and invalidate the adapter for future loads
                mWorkerQueue.removeMessages(0);
                mMainQueue.removeMessages(0);
                mMainQueue.removeMessages(0);


                for (int i = 0; i < mViewCache.length; ++i) {
                for (int i = 0; i < mViewCache.length; ++i) {