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

Commit dd60f4d2 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Optimizing memory for RemoteViewsAdapter caches.

The cache holds large number of remoteViews each of which have a
separate copy of applicationInfo.
Also simplifying some cache objects

Test: Manually tested on device
Change-Id: I353239354d329d3b0219ade55d39fe3b89e3bb02
parent a512d331
Loading
Loading
Loading
Loading
+12 −5
Original line number Diff line number Diff line
@@ -131,7 +131,7 @@ public class RemoteViews implements Parcelable, Filter {
     *
     * @hide
     */
    private ApplicationInfo mApplication;
    public ApplicationInfo mApplication;

    /**
     * The resource ID of the layout file. (Added to the parcel)
@@ -1519,8 +1519,7 @@ public class RemoteViews implements Parcelable, Filter {

        @Override
        public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
            return mNestedViews.mApplication.packageName.equals(parentInfo.packageName)
                    && mNestedViews.mApplication.uid == parentInfo.uid;
            return mNestedViews.hasSameAppInfo(parentInfo);
        }

        @Override
@@ -2138,8 +2137,7 @@ public class RemoteViews implements Parcelable, Filter {
        if (landscape == null || portrait == null) {
            throw new RuntimeException("Both RemoteViews must be non-null");
        }
        if (landscape.mApplication.uid != portrait.mApplication.uid
                || !landscape.mApplication.packageName.equals(portrait.mApplication.packageName)) {
        if (!landscape.hasSameAppInfo(portrait.mApplication)) {
            throw new RuntimeException("Both RemoteViews must share the same package and user");
        }
        mApplication = portrait.mApplication;
@@ -3554,6 +3552,15 @@ public class RemoteViews implements Parcelable, Filter {
        return applicationInfo;
    }

    /**
     * Returns true if the {@link #mApplication} is same as the provided info.
     *
     * @hide
     */
    public boolean hasSameAppInfo(ApplicationInfo info) {
        return mApplication.packageName.equals(info.packageName) && mApplication.uid == info.uid;
    }

    /**
     * Parcelable.Creator that instantiates RemoteViews objects
     */
+39 −30
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -114,6 +115,12 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
    // construction (happens when we have a cached FixedSizeRemoteViewsCache).
    private boolean mDataReady = false;

    /**
     * USed to dedupe {@link RemoteViews#mApplication} so that we do not hold on to
     * multiple copies of the same ApplicationInfo object.
     */
    private ApplicationInfo mLastRemoteViewAppInfo;

    /**
     * An interface for the RemoteAdapter to notify other classes when adapters
     * are actually connected to/disconnected from their actual services.
@@ -309,6 +316,8 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
    static class RemoteViewsFrameLayout extends AppWidgetHostView {
        private final FixedSizeRemoteViewsCache mCache;

        public int cacheIndex = -1;

        public RemoteViewsFrameLayout(Context context, FixedSizeRemoteViewsCache cache) {
            super(context);
            mCache = cache;
@@ -359,26 +368,23 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
     * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the
     * adapter that have not yet had their RemoteViews loaded.
     */
    private class RemoteViewsFrameLayoutRefSet {
        private final SparseArray<LinkedList<RemoteViewsFrameLayout>> mReferences =
                new SparseArray<>();
        private final HashMap<RemoteViewsFrameLayout, LinkedList<RemoteViewsFrameLayout>>
                mViewToLinkedList = new HashMap<>();
    private class RemoteViewsFrameLayoutRefSet
            extends SparseArray<LinkedList<RemoteViewsFrameLayout>> {

        /**
         * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter.
         */
        public void add(int position, RemoteViewsFrameLayout layout) {
            LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(position);
            LinkedList<RemoteViewsFrameLayout> refs = get(position);

            // Create the list if necessary
            if (refs == null) {
                refs = new LinkedList<RemoteViewsFrameLayout>();
                mReferences.put(position, refs);
                refs = new LinkedList<>();
                put(position, refs);
            }
            mViewToLinkedList.put(layout, refs);

            // Add the references to the list
            layout.cacheIndex = position;
            refs.add(layout);
        }

@@ -389,18 +395,13 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
        public void notifyOnRemoteViewsLoaded(int position, RemoteViews view) {
            if (view == null) return;

            final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(position);
            // Remove this set from the original mapping
            final LinkedList<RemoteViewsFrameLayout> refs = removeReturnOld(position);
            if (refs != null) {
                // Notify all the references for that position of the newly loaded RemoteViews
                for (final RemoteViewsFrameLayout ref : refs) {
                    ref.onRemoteViewsLoaded(view, mRemoteViewsOnClickHandler, true);
                    if (mViewToLinkedList.containsKey(ref)) {
                        mViewToLinkedList.remove(ref);
                    }
                }
                refs.clear();
                // Remove this set from the original mapping
                mReferences.remove(position);
            }
        }

@@ -408,20 +409,14 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
         * We need to remove views from this set if they have been recycled by the AdapterView.
         */
        public void removeView(RemoteViewsFrameLayout rvfl) {
            if (mViewToLinkedList.containsKey(rvfl)) {
                mViewToLinkedList.get(rvfl).remove(rvfl);
                mViewToLinkedList.remove(rvfl);
            if (rvfl.cacheIndex < 0) {
                return;
            }
            final LinkedList<RemoteViewsFrameLayout> refs = get(rvfl.cacheIndex);
            if (refs != null) {
                refs.remove(rvfl);
            }

        /**
         * Removes all references to all RemoteViewsFrameLayouts returned by the adapter.
         */
        public void clear() {
            // We currently just clear the references, and leave all the previous layouts returned
            // in their default state of the loading view.
            mReferences.clear();
            mViewToLinkedList.clear();
            rvfl.cacheIndex = -1;
        }
    }

@@ -534,7 +529,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
        // too much memory.
        private final SparseArray<RemoteViews> mIndexRemoteViews = new SparseArray<>();

        // An array of indices to load, Indices which are explicitely requested are set to true,
        // An array of indices to load, Indices which are explicitly requested are set to true,
        // and those determined by the preloading algorithm to prefetch are set to false.
        private final SparseBooleanArray mIndicesToLoad = new SparseBooleanArray();

@@ -994,6 +989,20 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
            return;
        }

        if (remoteViews.mApplication != null) {
            // We keep track of last application info. This helps when all the remoteViews have
            // same applicationInfo, which should be the case for a typical adapter. But if every
            // view has different application info, there will not be any optimization.
            if (mLastRemoteViewAppInfo != null
                    && remoteViews.hasSameAppInfo(mLastRemoteViewAppInfo)) {
                // We should probably also update the remoteViews for nested ViewActions.
                // Hopefully, RemoteViews in an adapter would be less complicated.
                remoteViews.mApplication = mLastRemoteViewAppInfo;
            } else {
                mLastRemoteViewAppInfo = remoteViews.mApplication;
            }
        }

        int layoutId = remoteViews.getLayoutId();
        RemoteViewsMetaData metaData = mCache.getMetaData();
        boolean viewTypeInRange;