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

Commit 04dfe0d2 authored by Winson Chung's avatar Winson Chung
Browse files

Simplifying memory management, use Task Keys as resource cache keys.

- Attempts to load non-topmost task thumbnails from cache
- Ensuring that we release all references to the activity from the bg loader
- Removes background loading debug flag
- Moving callbacks into their respective classes
- cleaning up some callbacks when data is loaded in the bg

Change-Id: Ibb968349d08084922d5b28e432b76a165bf20d6b
parent 4d7b092a
Loading
Loading
Loading
Loading
+4 −6
Original line number Diff line number Diff line
@@ -28,10 +28,10 @@ public class Constants {
        public static class App {
            public static final boolean EnableTaskFiltering = false;
            public static final boolean EnableTaskStackClipping = false;
            public static final boolean EnableBackgroundTaskLoading = true;
            public static final boolean ForceDisableBackgroundCache = false;
            // This disables the bitmap and icon caches to
            public static final boolean DisableBackgroundCache = false;

            public static final boolean TaskDataLoader = false;
            public static final boolean TaskDataLoader = true;
            public static final boolean SystemUIHandshake = false;
            public static final boolean TimeSystemCalls = false;
            public static final boolean Memory = false;
@@ -43,7 +43,7 @@ public class Constants {
            public static final boolean TouchEvents = false;
            public static final boolean MeasureAndLayout = false;
            public static final boolean Clipping = false;
            public static final boolean HwLayers = false;
            public static final boolean HwLayers = true;
        }

        public static class TaskStack {
@@ -74,8 +74,6 @@ public class Constants {
            public static class Animation {
                public static final int TaskRemovedReshuffleDuration = 200;
                public static final int SnapScrollBackDuration = 650;
                public static final int SwipeDismissDuration = 350;
                public static final int SwipeSnapBackDuration = 350;
            }

            // The padding will be applied to the smallest dimension, and then applied to all sides
+1 −2
Original line number Diff line number Diff line
@@ -45,7 +45,6 @@ public class RecentsActivity extends Activity {
        SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
        ArrayList<TaskStack> stacks = root.getStacks();
        if (!stacks.isEmpty()) {
            // XXX: We just replace the root every time for now, we will change this in the future
            mRecentsView.setBSP(root);
        }

@@ -155,7 +154,7 @@ public class RecentsActivity extends Activity {
                Console.AnsiRed);
        super.onPause();

        // Stop the loader when we leave Recents
        // Stop the loader immediately when we leave Recents
        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
        loader.stopLoader();
    }
+119 −132
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import com.android.systemui.recents.model.TaskStack;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;


@@ -84,7 +83,9 @@ class TaskResourceLoader implements Runnable {
    TaskResourceLoadQueue mLoadQueue;
    DrawableLruCache mIconCache;
    BitmapLruCache mThumbnailCache;

    boolean mCancelled;
    boolean mWaitingOnLoadQueue;

    /** Constructor, creates a new loading thread that loads task resources in the background */
    public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache iconCache,
@@ -116,6 +117,11 @@ class TaskResourceLoader implements Runnable {
        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|stop]");
        // Mark as cancelled for the thread to pick up
        mCancelled = true;
        // If we are waiting for the load queue for more tasks, then we can just reset the
        // Context now, since nothing is using it
        if (mWaitingOnLoadQueue) {
            mContext = null;
        }
    }

    @Override
@@ -124,6 +130,8 @@ class TaskResourceLoader implements Runnable {
            Console.log(Constants.DebugFlags.App.TaskDataLoader,
                    "[TaskResourceLoader|run|" + Thread.currentThread().getId() + "]");
            if (mCancelled) {
                Console.log(Constants.DebugFlags.App.TaskDataLoader,
                        "[TaskResourceLoader|cancel|" + Thread.currentThread().getId() + "]");
                // We have to unset the context here, since the background thread may be using it
                // when we call stop()
                mContext = null;
@@ -142,50 +150,52 @@ class TaskResourceLoader implements Runnable {
                final Task t = mLoadQueue.nextTask();
                if (t != null) {
                    try {
                        Drawable cachedIcon = mIconCache.get(t);
                        Bitmap cachedThumbnail = mThumbnailCache.get(t);
                        Drawable loadIcon = mIconCache.get(t.key);
                        Bitmap loadThumbnail = mThumbnailCache.get(t.key);
                        Console.log(Constants.DebugFlags.App.TaskDataLoader,
                                "  [TaskResourceLoader|load]",
                                t + " icon: " + cachedIcon + " thumbnail: " + cachedThumbnail);
                                t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail);
                        // Load the icon
                        if (cachedIcon == null) {
                        if (loadIcon == null) {
                            PackageManager pm = mContext.getPackageManager();
                            ActivityInfo info = pm.getActivityInfo(t.intent.getComponent(),
                            ActivityInfo info = pm.getActivityInfo(t.key.intent.getComponent(),
                                    PackageManager.GET_META_DATA);
                            Drawable icon = info.loadIcon(pm);
                            if (!mCancelled) {
                                Console.log(Constants.DebugFlags.App.TaskDataLoader,
                                        "    [TaskResourceLoader|loadIcon]",
                                        icon);
                                t.icon = icon;
                                mIconCache.put(t, icon);
                                loadIcon = icon;
                                mIconCache.put(t.key, icon);
                            }
                        }
                        // Load the thumbnail
                        if (cachedThumbnail == null) {
                        if (loadThumbnail == null) {
                            ActivityManager am = (ActivityManager)
                                    mContext.getSystemService(Context.ACTIVITY_SERVICE);
                            Bitmap thumbnail = am.getTaskTopThumbnail(t.id);
                            Bitmap thumbnail = am.getTaskTopThumbnail(t.key.id);
                            if (!mCancelled) {
                                if (thumbnail != null) {
                                    Console.log(Constants.DebugFlags.App.TaskDataLoader,
                                            "    [TaskResourceLoader|loadThumbnail]",
                                            thumbnail);
                                    t.thumbnail = thumbnail;
                                    mThumbnailCache.put(t, thumbnail);
                                    loadThumbnail = thumbnail;
                                    mThumbnailCache.put(t.key, thumbnail);
                                } else {
                                    Console.logError(mContext,
                                            "Failed to load task top thumbnail for: " +
                                                    t.intent.getComponent().getPackageName());
                                                    t.key.intent.getComponent().getPackageName());
                                }
                            }
                        }
                        if (!mCancelled) {
                            // Notify that the task data has changed
                            final Drawable newIcon = loadIcon;
                            final Bitmap newThumbnail = loadThumbnail;
                            mMainThreadHandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    t.notifyTaskDataChanged();
                                    t.notifyTaskDataLoaded(newThumbnail, newIcon);
                                }
                            });
                        }
@@ -200,7 +210,9 @@ class TaskResourceLoader implements Runnable {
                        try {
                            Console.log(Constants.DebugFlags.App.TaskDataLoader,
                                    "[TaskResourceLoader|waitOnLoadQueue]");
                            mWaitingOnLoadQueue = true;
                            mLoadQueue.wait();
                            mWaitingOnLoadQueue = false;
                        } catch (InterruptedException ie) {
                            ie.printStackTrace();
                        }
@@ -211,14 +223,17 @@ class TaskResourceLoader implements Runnable {
    }
}

/** The drawable cache */
class DrawableLruCache extends LruCache<Task, Drawable> {
/**
 * The drawable cache.  By using the Task's key, we can prevent holding onto a reference to the Task
 * resource data, while keeping the cache data in memory where necessary.
 */
class DrawableLruCache extends LruCache<Task.TaskKey, Drawable> {
    public DrawableLruCache(int cacheSize) {
        super(cacheSize);
    }

    @Override
    protected int sizeOf(Task t, Drawable d) {
    protected int sizeOf(Task.TaskKey t, Drawable d) {
        // The cache size will be measured in kilobytes rather than number of items
        // NOTE: this isn't actually correct, as the icon may be smaller
        int maxBytes = (d.getIntrinsicWidth() * d.getIntrinsicHeight() * 4);
@@ -226,14 +241,17 @@ class DrawableLruCache extends LruCache<Task, Drawable> {
    }
}

/** The bitmap cache */
class BitmapLruCache extends LruCache<Task, Bitmap> {
/**
 * The bitmap cache.  By using the Task's key, we can prevent holding onto a reference to the Task
 * resource data, while keeping the cache data in memory where necessary.
 */
class BitmapLruCache extends LruCache<Task.TaskKey, Bitmap> {
    public BitmapLruCache(int cacheSize) {
        super(cacheSize);
    }

    @Override
    protected int sizeOf(Task t, Bitmap bitmap) {
    protected int sizeOf(Task.TaskKey t, Bitmap bitmap) {
        // The cache size will be measured in kilobytes rather than number of items
        return bitmap.getAllocationByteCount() / 1024;
    }
@@ -257,16 +275,18 @@ public class RecentsTaskLoader {

    /** Private Constructor */
    private RecentsTaskLoader(Context context) {
        // Calculate the cache sizes
        // Calculate the cache sizes, we just use a reasonable number here similar to those
        // suggested in the Android docs, 1/8th for the thumbnail cache and 1/32 of the max memory
        // for icons.
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        mMaxThumbnailCacheSize = maxMemory / 8;
        mMaxIconCacheSize = mMaxThumbnailCacheSize / 4;
        int iconCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 :
        int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
                mMaxIconCacheSize;
        int thumbnailCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 :
        int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
                mMaxThumbnailCacheSize;

        Console.log(Constants.DebugFlags.App.EnableBackgroundTaskLoading,
        Console.log(Constants.DebugFlags.App.TaskDataLoader,
                "[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize +
                " iconCache: " + iconCacheSize);

@@ -351,36 +371,34 @@ public class RecentsTaskLoader {
                ActivityInfo info = pm.getActivityInfo(t.baseIntent.getComponent(),
                        PackageManager.GET_META_DATA);
                String title = info.loadLabel(pm).toString();
                Task task;
                boolean isForemostTask = (i == (taskCount - 1));

                // Preload the specified number of apps
                if (i >= (taskCount - preloadCount) ||
                        !Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
                if (i >= (taskCount - preloadCount)) {
                    Console.log(Constants.DebugFlags.App.TaskDataLoader,
                            "[RecentsTaskLoader|preloadTask]",
                            "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());

                    task = new Task(t.persistentId, t.baseIntent, title, null, null);
                    Task task = new Task(t.persistentId, t.baseIntent, title);

                    // Load the icon (if possible from the cache)
                    if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
                        task.icon = mIconCache.get(task);
                    // Load the icon (if possible and not the foremost task, from the cache)
                    if (!isForemostTask) {
                        task.icon = mIconCache.get(task.key);
                    }
                    if (task.icon == null) {
                        task.icon = info.loadIcon(pm);
                        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
                            mIconCache.put(task, task.icon);
                        }
                        mIconCache.put(task.key, task.icon);
                    }

                    // Load the thumbnail (if possible from the cache)
                    if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
                        task.thumbnail = mThumbnailCache.get(task);
                    // Load the thumbnail (if possible and not the foremost task, from the cache)
                    if (!isForemostTask) {
                        task.thumbnail = mThumbnailCache.get(task.key);
                    }
                    if (task.thumbnail == null) {
                        Console.log(Constants.DebugFlags.App.TaskDataLoader,
                                "[RecentsTaskLoader|loadingTaskThumbnail]");
                        task.thumbnail = am.getTaskTopThumbnail(t.id);
                        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
                            mThumbnailCache.put(task, task.thumbnail);
                        }
                        mThumbnailCache.put(task.key, task.thumbnail);
                    }

                    // Create as many tasks a we want to multiply by
@@ -394,19 +412,9 @@ public class RecentsTaskLoader {
                    for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
                        Console.log(Constants.DebugFlags.App.TaskDataLoader,
                                "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
                        task = new Task(t.persistentId, t.baseIntent, title, null, null);
                        stack.addTask(task);
                        stack.addTask(new Task(t.persistentId, t.baseIntent, title));
                    }
                }

                /*
                if (stacks.containsKey(t.stackId)) {
                    builder = stacks.get(t.stackId);
                } else {
                    builder = new TaskStackBuilder();
                    stacks.put(t.stackId, builder);
                }
                */
            }
            Console.log(Constants.DebugFlags.App.TimeSystemCalls,
                    "[RecentsTaskLoader|getAllTaskTopThumbnail]",
@@ -428,15 +436,17 @@ public class RecentsTaskLoader {
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Start the task loader
        mLoader.start(context);

        return root;
    }

    /** Acquires the task resource data from the pool. */
    public void loadTaskData(Task t) {
        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
            Drawable icon = mIconCache.get(t);
            Bitmap thumbnail = mThumbnailCache.get(t);
        Drawable icon = mIconCache.get(t.key);
        Bitmap thumbnail = mThumbnailCache.get(t.key);

        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
                t + " icon: " + icon + " thumbnail: " + thumbnail +
@@ -454,38 +464,31 @@ public class RecentsTaskLoader {
        if (requiresLoad) {
            mLoadQueue.addTask(t);
        }
            t.notifyTaskLoaded(thumbnail, icon);
        }
        t.notifyTaskDataLoaded(thumbnail, icon);
    }

    /** Releases the task resource data back into the pool. */
    public void unloadTaskData(Task t) {
        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
        Console.log(Constants.DebugFlags.App.TaskDataLoader,
                "[RecentsTaskLoader|unloadTask]", t +
                " thumbnailCacheSize: " + mThumbnailCache.size());

        mLoadQueue.removeTask(t);
            t.notifyTaskUnloaded(mDefaultThumbnail, mDefaultIcon);
        } else {
            t.notifyTaskUnloaded(null, null);
        }
        t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon);
    }

    /** Completely removes the resource data from the pool. */
    public void deleteTaskData(Task t) {
        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
        Console.log(Constants.DebugFlags.App.TaskDataLoader,
                "[RecentsTaskLoader|deleteTask]", t);

        mLoadQueue.removeTask(t);
            mThumbnailCache.remove(t);
            mIconCache.remove(t);
            t.notifyTaskUnloaded(mDefaultThumbnail, mDefaultIcon);
        } else {
            t.notifyTaskUnloaded(null, null);
        }
        mThumbnailCache.remove(t.key);
        mIconCache.remove(t.key);
        t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon);
    }

    /** Stops the task loader */
    /** Stops the task loader and clears all pending tasks */
    void stopLoader() {
        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stopLoader]");
        mLoader.stop();
@@ -496,25 +499,10 @@ public class RecentsTaskLoader {
        Console.log(Constants.DebugFlags.App.Memory, "[RecentsTaskLoader|onTrimMemory]",
                Console.trimMemoryLevelToString(level));

        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
            // If we are hidden, then we should unload each of the task keys
            if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
                Console.log(Constants.DebugFlags.App.Memory, "[RecentsTaskLoader|unloadTasks]"
                );
                // Unload each of the keys in the thumbnail cache
                Map<Task, Bitmap> thumbnailCache = mThumbnailCache.snapshot();
                for (Task t : thumbnailCache.keySet()) {
                    unloadTaskData(t);
                }
                // As well as the keys in the icon cache
                Map<Task, Drawable> iconCache = mIconCache.snapshot();
                for (Task t : iconCache.keySet()) {
                    unloadTaskData(t);
                }
            }

        switch (level) {
            case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
                // Do nothing
                break;
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
            case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
                // We are leaving recents, so trim the data a bit
@@ -538,4 +526,3 @@ public class RecentsTaskLoader {
        }
    }
}
}
+9 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.recents.model;

import android.content.Context;
import android.graphics.Rect;

import java.util.ArrayList;

@@ -26,6 +27,14 @@ import java.util.ArrayList;
 * stacks should be placed.
 */
public class SpaceNode {
    /* BSP node callbacks */
    public interface SpaceNodeCallbacks {
        /** Notifies when a node is added */
        public void onSpaceNodeAdded(SpaceNode node);
        /** Notifies when a node is measured */
        public void onSpaceNodeMeasured(SpaceNode node, Rect rect);
    }

    Context mContext;

    SpaceNode mStartNode;
+0 −28
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.recents.model;

import android.graphics.Rect;


/* BSP node callbacks */
public interface SpaceNodeCallbacks {
    /** Notifies when a node is added */
    public void onSpaceNodeAdded(SpaceNode node);
    /** Notifies when a node is measured */
    public void onSpaceNodeMeasured(SpaceNode node, Rect rect);
}
 No newline at end of file
Loading