Skip to content

Commit 26ed04b

Browse files
Adam CohenAndroid (Google) Code Review
authored andcommitted
Merge "Caching the FixedSizeRemoteViewsCaches across rotation" into jb-dev
2 parents cb1aec3 + 335c3b6 commit 26ed04b

File tree

3 files changed

+215
-7
lines changed

3 files changed

+215
-7
lines changed

core/java/android/widget/AbsListView.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package android.widget;
1818

19-
import com.android.internal.R;
20-
2119
import android.content.Context;
2220
import android.content.Intent;
2321
import android.content.res.TypedArray;
@@ -69,6 +67,8 @@
6967
import android.view.inputmethod.InputConnectionWrapper;
7068
import android.view.inputmethod.InputMethodManager;
7169

70+
import com.android.internal.R;
71+
7272
import java.util.ArrayList;
7373
import java.util.List;
7474

@@ -1813,6 +1813,10 @@ public Parcelable onSaveInstanceState() {
18131813
}
18141814
ss.checkedItemCount = mCheckedItemCount;
18151815

1816+
if (mRemoteAdapter != null) {
1817+
mRemoteAdapter.saveRemoteViewsCache();
1818+
}
1819+
18161820
return ss;
18171821
}
18181822

@@ -5974,6 +5978,9 @@ public void setRemoteViewsAdapter(Intent intent) {
59745978
mDeferNotifyDataSetChanged = false;
59755979
// Otherwise, create a new RemoteViewsAdapter for binding
59765980
mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this);
5981+
if (mRemoteAdapter.isDataReady()) {
5982+
setAdapter(mRemoteAdapter);
5983+
}
59775984
}
59785985

59795986
/**

core/java/android/widget/AdapterViewAnimator.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,9 @@ public SavedState[] newArray(int size) {
813813
@Override
814814
public Parcelable onSaveInstanceState() {
815815
Parcelable superState = super.onSaveInstanceState();
816+
if (mRemoteViewsAdapter != null) {
817+
mRemoteViewsAdapter.saveRemoteViewsCache();
818+
}
816819
return new SavedState(superState, mWhichChild);
817820
}
818821

@@ -984,6 +987,9 @@ public void setRemoteViewsAdapter(Intent intent) {
984987
mDeferNotifyDataSetChanged = false;
985988
// Otherwise, create a new RemoteViewsAdapter for binding
986989
mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this);
990+
if (mRemoteViewsAdapter.isDataReady()) {
991+
setAdapter(mRemoteViewsAdapter);
992+
}
987993
}
988994

989995
@Override

core/java/android/widget/RemoteViewsAdapter.java

Lines changed: 200 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,16 @@
2929
import android.os.IBinder;
3030
import android.os.Looper;
3131
import android.os.Message;
32+
import android.os.Parcel;
33+
import android.os.Parcelable;
3234
import android.os.RemoteException;
3335
import android.util.Log;
36+
import android.util.Pair;
3437
import android.view.LayoutInflater;
3538
import android.view.View;
3639
import android.view.View.MeasureSpec;
3740
import android.view.ViewGroup;
41+
import android.widget.RemoteViewsService.RemoteViewsFactory;
3842

3943
import com.android.internal.widget.IRemoteViewsAdapterConnection;
4044
import com.android.internal.widget.IRemoteViewsFactory;
@@ -83,6 +87,26 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
8387
private Handler mWorkerQueue;
8488
private Handler mMainQueue;
8589

90+
// We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data
91+
// structures;
92+
private static final HashMap<Pair<Intent.FilterComparison, Integer>, Parcel>
93+
sCachedRemoteViewsCaches = new HashMap<Pair<Intent.FilterComparison, Integer>,
94+
Parcel>();
95+
private static final HashMap<Pair<Intent.FilterComparison, Integer>, Runnable>
96+
sRemoteViewsCacheRemoveRunnables = new HashMap<Pair<Intent.FilterComparison, Integer>,
97+
Runnable>();
98+
private static HandlerThread sCacheRemovalThread;
99+
private static Handler sCacheRemovalQueue;
100+
101+
// We keep the cache around for a duration after onSaveInstanceState for use on re-inflation.
102+
// If a new RemoteViewsAdapter with the same intent / widget id isn't constructed within this
103+
// duration, the cache is dropped.
104+
private static final int REMOTE_VIEWS_CACHE_DURATION = 5000;
105+
106+
// Used to indicate to the AdapterView that it can use this Adapter immediately after
107+
// construction (happens when we have a cached FixedSizeRemoteViewsCache).
108+
private boolean mDataReady = false;
109+
86110
/**
87111
* An interface for the RemoteAdapter to notify other classes when adapters
88112
* are actually connected to/disconnected from their actual services.
@@ -331,7 +355,7 @@ public void clear() {
331355
/**
332356
* The meta-data associated with the cache in it's current state.
333357
*/
334-
private static class RemoteViewsMetaData {
358+
private static class RemoteViewsMetaData implements Parcelable {
335359
int count;
336360
int viewTypeCount;
337361
boolean hasStableIds;
@@ -350,6 +374,51 @@ public RemoteViewsMetaData() {
350374
reset();
351375
}
352376

377+
public RemoteViewsMetaData(Parcel src) {
378+
count = src.readInt();
379+
viewTypeCount = src.readInt();
380+
hasStableIds = src.readInt() == 0 ? false : true;
381+
mFirstViewHeight = src.readInt();
382+
if (src.readInt() != 0) {
383+
mUserLoadingView = new RemoteViews(src);
384+
}
385+
if (src.readInt() != 0) {
386+
mFirstView = new RemoteViews(src);
387+
}
388+
int count = src.readInt();
389+
for (int i = 0; i < count; i++) {
390+
mTypeIdIndexMap.put(src.readInt(), src.readInt());
391+
}
392+
}
393+
394+
@Override
395+
public void writeToParcel(Parcel dest, int flags) {
396+
dest.writeInt(count);
397+
dest.writeInt(viewTypeCount);
398+
dest.writeInt(hasStableIds ? 1 : 0);
399+
dest.writeInt(mFirstViewHeight);
400+
dest.writeInt(mUserLoadingView != null ? 1 : 0);
401+
if (mUserLoadingView != null) {
402+
mUserLoadingView.writeToParcel(dest, flags);
403+
}
404+
dest.writeInt(mFirstView != null ? 1 : 0);
405+
if (mFirstView != null) {
406+
mFirstView.writeToParcel(dest, flags);
407+
}
408+
409+
int count = mTypeIdIndexMap.size();
410+
dest.writeInt(count);
411+
for (Integer key: mTypeIdIndexMap.keySet()) {
412+
dest.writeInt(key);
413+
dest.writeInt(mTypeIdIndexMap.get(key));
414+
}
415+
}
416+
417+
@Override
418+
public int describeContents() {
419+
return 0;
420+
}
421+
353422
public void set(RemoteViewsMetaData d) {
354423
synchronized (d) {
355424
count = d.count;
@@ -460,7 +529,7 @@ private RemoteViewsFrameLayout createLoadingView(int position, View convertView,
460529
/**
461530
* The meta-data associated with a single item in the cache.
462531
*/
463-
private static class RemoteViewsIndexMetaData {
532+
private static class RemoteViewsIndexMetaData implements Parcelable {
464533
int typeId;
465534
long itemId;
466535
boolean isRequested;
@@ -469,6 +538,22 @@ public RemoteViewsIndexMetaData(RemoteViews v, long itemId, boolean requested) {
469538
set(v, itemId, requested);
470539
}
471540

541+
public RemoteViewsIndexMetaData(Parcel src) {
542+
typeId = src.readInt();
543+
itemId = src.readLong();
544+
}
545+
546+
@Override
547+
public void writeToParcel(Parcel dest, int flags) {
548+
dest.writeInt(typeId);
549+
dest.writeLong(itemId);
550+
}
551+
552+
@Override
553+
public int describeContents() {
554+
return 0;
555+
}
556+
472557
public void set(RemoteViews v, long id, boolean requested) {
473558
itemId = id;
474559
if (v != null) {
@@ -478,12 +563,14 @@ public void set(RemoteViews v, long id, boolean requested) {
478563
}
479564
isRequested = requested;
480565
}
566+
567+
481568
}
482569

483570
/**
484571
*
485572
*/
486-
private static class FixedSizeRemoteViewsCache {
573+
private static class FixedSizeRemoteViewsCache implements Parcelable {
487574
private static final String TAG = "FixedSizeRemoteViewsCache";
488575

489576
// The meta data related to all the RemoteViews, ie. count, is stable, etc.
@@ -545,6 +632,57 @@ public FixedSizeRemoteViewsCache(int maxCacheSize) {
545632
mLoadIndices = new HashSet<Integer>();
546633
}
547634

635+
public FixedSizeRemoteViewsCache(Parcel src) {
636+
mMaxCount = src.readInt();
637+
mMaxCountSlack = src.readInt();
638+
mPreloadLowerBound = src.readInt();
639+
mPreloadUpperBound = src.readInt();
640+
mMetaData = new RemoteViewsMetaData(src);
641+
int count = src.readInt();
642+
mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>();
643+
for (int i = 0; i < count; i++) {
644+
mIndexMetaData.put(src.readInt(), new RemoteViewsIndexMetaData(src));
645+
}
646+
count = src.readInt();
647+
mIndexRemoteViews = new HashMap<Integer, RemoteViews>();
648+
for (int i = 0; i < count; i++) {
649+
mIndexRemoteViews.put(src.readInt(), new RemoteViews(src));
650+
}
651+
652+
mTemporaryMetaData = new RemoteViewsMetaData();
653+
mRequestedIndices = new HashSet<Integer>();
654+
mLastRequestedIndex = -1;
655+
mLoadIndices = new HashSet<Integer>();
656+
}
657+
658+
@Override
659+
public void writeToParcel(Parcel dest, int flags) {
660+
dest.writeInt(mMaxCount);
661+
dest.writeInt(mMaxCountSlack);
662+
dest.writeInt(mPreloadLowerBound);
663+
dest.writeInt(mPreloadUpperBound);
664+
mMetaData.writeToParcel(dest, 0);
665+
666+
// We write the index data and cache
667+
int count = mIndexMetaData.size();
668+
dest.writeInt(count);
669+
for (Integer key: mIndexMetaData.keySet()) {
670+
dest.writeInt(key);
671+
mIndexMetaData.get(key).writeToParcel(dest, flags);
672+
}
673+
count = mIndexRemoteViews.size();
674+
dest.writeInt(count);
675+
for (Integer key: mIndexRemoteViews.keySet()) {
676+
dest.writeInt(key);
677+
mIndexRemoteViews.get(key).writeToParcel(dest, flags);
678+
}
679+
}
680+
681+
@Override
682+
public int describeContents() {
683+
return 0;
684+
}
685+
548686
public void insert(int position, RemoteViews v, long itemId, boolean isRequested) {
549687
// Trim the cache if we go beyond the count
550688
if (mIndexRemoteViews.size() >= mMaxCount) {
@@ -747,11 +885,30 @@ public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectio
747885
mWorkerQueue = new Handler(mWorkerThread.getLooper());
748886
mMainQueue = new Handler(Looper.myLooper(), this);
749887

888+
if (sCacheRemovalThread == null) {
889+
sCacheRemovalThread = new HandlerThread("RemoteViewsAdapter-cachePruner");
890+
sCacheRemovalThread.start();
891+
sCacheRemovalQueue = new Handler(sCacheRemovalThread.getLooper());
892+
}
893+
750894
// Initialize the cache and the service connection on startup
751-
mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize);
752895
mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
753896
mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
754-
requestBindService();
897+
898+
Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison, Integer>
899+
(new Intent.FilterComparison(mIntent), mAppWidgetId);
900+
901+
synchronized(sCachedRemoteViewsCaches) {
902+
if (sCachedRemoteViewsCaches.containsKey(key)) {
903+
Parcel src = sCachedRemoteViewsCaches.get(key);
904+
src.setDataPosition(0);
905+
mCache = new FixedSizeRemoteViewsCache(src);
906+
mDataReady = true;
907+
} else {
908+
mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize);
909+
requestBindService();
910+
}
911+
}
755912
}
756913

757914
@Override
@@ -765,6 +922,44 @@ protected void finalize() throws Throwable {
765922
}
766923
}
767924

925+
public boolean isDataReady() {
926+
return mDataReady;
927+
}
928+
929+
public void saveRemoteViewsCache() {
930+
final Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison,
931+
Integer> (new Intent.FilterComparison(mIntent), mAppWidgetId);
932+
933+
synchronized(sCachedRemoteViewsCaches) {
934+
// If we already have a remove runnable posted for this key, remove it.
935+
if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) {
936+
sCacheRemovalQueue.removeCallbacks(sRemoteViewsCacheRemoveRunnables.get(key));
937+
sRemoteViewsCacheRemoveRunnables.remove(key);
938+
}
939+
940+
Parcel p = Parcel.obtain();
941+
synchronized(mCache) {
942+
mCache.writeToParcel(p, 0);
943+
}
944+
sCachedRemoteViewsCaches.put(key, p);
945+
Runnable r = new Runnable() {
946+
@Override
947+
public void run() {
948+
synchronized (sCachedRemoteViewsCaches) {
949+
if (sCachedRemoteViewsCaches.containsKey(key)) {
950+
sCachedRemoteViewsCaches.remove(key);
951+
}
952+
if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) {
953+
sRemoteViewsCacheRemoveRunnables.remove(key);
954+
}
955+
}
956+
}
957+
};
958+
sRemoteViewsCacheRemoveRunnables.put(key, r);
959+
sCacheRemovalQueue.postDelayed(r, REMOTE_VIEWS_CACHE_DURATION);
960+
}
961+
}
962+
768963
private void loadNextIndexInBackground() {
769964
mWorkerQueue.post(new Runnable() {
770965
@Override

0 commit comments

Comments
 (0)