2929import android .os .IBinder ;
3030import android .os .Looper ;
3131import android .os .Message ;
32+ import android .os .Parcel ;
33+ import android .os .Parcelable ;
3234import android .os .RemoteException ;
3335import android .util .Log ;
36+ import android .util .Pair ;
3437import android .view .LayoutInflater ;
3538import android .view .View ;
3639import android .view .View .MeasureSpec ;
3740import android .view .ViewGroup ;
41+ import android .widget .RemoteViewsService .RemoteViewsFactory ;
3842
3943import com .android .internal .widget .IRemoteViewsAdapterConnection ;
4044import 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