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

Commit eea02253 authored by Winson Chung's avatar Winson Chung
Browse files

Fixing minor memory leak with name comparator.

- WidgetsAndShortcutNameComparator was using the actual widget and shortcut resolve
  infos as the key to the label cache.  Neither of these classes override hashCode()
  and we were retrieving a new set of widgets and shortcuts whenever packages changed
  so we would end up creating more and more entries in the cache.  This isn't a huge
  leak, but could lead to problems if Launcher is used for long periods without being
  killed.
- Now, we use a ComponentKey as the key, so that we don't keep a reference to the
  widget/shortcut infos and also ensures that they should hash to the same labels.

Change-Id: I91347ee72363adbc2b075b67dba331e35ab1fe34
parent a9095e07
Loading
Loading
Loading
Loading
+57 −28
Original line number Original line Diff line number Diff line
package com.android.launcher3.model;
package com.android.launcher3.model;


import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ResolveInfo;

import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.util.ComponentKey;


import java.text.Collator;
import java.text.Collator;
import java.util.Comparator;
import java.util.Comparator;
@@ -16,53 +17,81 @@ import java.util.HashMap;
public class WidgetsAndShortcutNameComparator implements Comparator<Object> {
public class WidgetsAndShortcutNameComparator implements Comparator<Object> {
    private final AppWidgetManagerCompat mManager;
    private final AppWidgetManagerCompat mManager;
    private final PackageManager mPackageManager;
    private final PackageManager mPackageManager;
    private final HashMap<Object, String> mLabelCache;
    private final HashMap<ComponentKey, String> mLabelCache;
    private final Collator mCollator;
    private final Collator mCollator;
    private final UserHandleCompat mMainHandle;
    private final UserHandleCompat mMainHandle;


    public WidgetsAndShortcutNameComparator(Context context) {
    public WidgetsAndShortcutNameComparator(Context context) {
        mManager = AppWidgetManagerCompat.getInstance(context);
        mManager = AppWidgetManagerCompat.getInstance(context);
        mPackageManager = context.getPackageManager();
        mPackageManager = context.getPackageManager();
        mLabelCache = new HashMap<Object, String>();
        mLabelCache = new HashMap<>();
        mCollator = Collator.getInstance();
        mCollator = Collator.getInstance();
        mMainHandle = UserHandleCompat.myUserHandle();
        mMainHandle = UserHandleCompat.myUserHandle();
    }
    }


    @Override
    /**
    public final int compare(Object a, Object b) {
     * Resets any stored state.
        String labelA, labelB;
     */
        if (mLabelCache.containsKey(a)) {
    public void reset() {
            labelA = mLabelCache.get(a);
        mLabelCache.clear();
        } else {
            labelA = (a instanceof LauncherAppWidgetProviderInfo)
                    ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a))
                    : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager));
            mLabelCache.put(a, labelA);
        }
        if (mLabelCache.containsKey(b)) {
            labelB = mLabelCache.get(b);
        } else {
            labelB = (b instanceof LauncherAppWidgetProviderInfo)
                    ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b))
                    : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager));
            mLabelCache.put(b, labelB);
    }
    }


        // Currently, there is no work profile shortcuts, hence only considering the widget cases.
    @Override

    public final int compare(Object objA, Object objB) {
        boolean aWorkProfile = (a instanceof LauncherAppWidgetProviderInfo) &&
        ComponentKey keyA = getComponentKey(objA);
                !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) a));
        ComponentKey keyB = getComponentKey(objB);
        boolean bWorkProfile = (b instanceof LauncherAppWidgetProviderInfo) &&
                !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) b));


        // Independent of how the labels compare, if only one of the two widget info belongs to
        // Independent of how the labels compare, if only one of the two widget info belongs to
        // work profile, put that one in the back.
        // work profile, put that one in the back.
        boolean aWorkProfile = !mMainHandle.equals(keyA.user);
        boolean bWorkProfile = !mMainHandle.equals(keyB.user);
        if (aWorkProfile && !bWorkProfile) {
        if (aWorkProfile && !bWorkProfile) {
            return 1;
            return 1;
        }
        }
        if (!aWorkProfile && bWorkProfile) {
        if (!aWorkProfile && bWorkProfile) {
            return -1;
            return -1;
        }
        }

        // Get the labels for comparison
        String labelA = mLabelCache.get(keyA);
        String labelB = mLabelCache.get(keyB);
        if (labelA == null) {
            labelA = getLabel(objA);
            mLabelCache.put(keyA, labelA);
        }
        if (labelB == null) {
            labelB = getLabel(objB);
            mLabelCache.put(keyB, labelB);
        }
        return mCollator.compare(labelA, labelB);
        return mCollator.compare(labelA, labelB);
    }
    }

    /**
     * @return a component key for the given widget or shortcut info.
     */
    private ComponentKey getComponentKey(Object o) {
        if (o instanceof LauncherAppWidgetProviderInfo) {
            LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
            return new ComponentKey(widgetInfo.provider, mManager.getUser(widgetInfo));
        } else {
            ResolveInfo shortcutInfo = (ResolveInfo) o;
            ComponentName cn = new ComponentName(shortcutInfo.activityInfo.packageName,
                    shortcutInfo.activityInfo.name);
            // Currently, there are no work profile shortcuts
            return new ComponentKey(cn, UserHandleCompat.myUserHandle());
        }
    }

    /**
     * @return the label for the given widget or shortcut info.  This may be an expensive call.
     */
    private String getLabel(Object o) {
        if (o instanceof LauncherAppWidgetProviderInfo) {
            LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
            return Utilities.trim(mManager.loadLabel(widgetInfo));
        } else {
            ResolveInfo shortcutInfo = (ResolveInfo) o;
            return Utilities.trim(shortcutInfo.loadLabel(mPackageManager));
        }
    }
};
};
+3 −2
Original line number Original line Diff line number Diff line
@@ -39,7 +39,7 @@ public class WidgetsModel {
    private ArrayList<Object> mRawList;
    private ArrayList<Object> mRawList;


    private final AppWidgetManagerCompat mAppWidgetMgr;
    private final AppWidgetManagerCompat mAppWidgetMgr;
    private final Comparator mWidgetAndShortcutNameComparator;
    private final WidgetsAndShortcutNameComparator mWidgetAndShortcutNameComparator;
    private final Comparator mAppNameComparator;
    private final Comparator mAppNameComparator;
    private final IconCache mIconCache;
    private final IconCache mIconCache;
    private final AppFilter mAppFilter;
    private final AppFilter mAppFilter;
@@ -103,6 +103,7 @@ public class WidgetsModel {
        // clear the lists.
        // clear the lists.
        mWidgetsList.clear();
        mWidgetsList.clear();
        mPackageItemInfos.clear();
        mPackageItemInfos.clear();
        mWidgetAndShortcutNameComparator.reset();


        // add and update.
        // add and update.
        for (Object o: rawWidgetsShortcuts) {
        for (Object o: rawWidgetsShortcuts) {
@@ -139,7 +140,7 @@ public class WidgetsModel {
            if (widgetsShortcutsList != null) {
            if (widgetsShortcutsList != null) {
                widgetsShortcutsList.add(o);
                widgetsShortcutsList.add(o);
            } else {
            } else {
                widgetsShortcutsList = new ArrayList<Object>();
                widgetsShortcutsList = new ArrayList<>();
                widgetsShortcutsList.add(o);
                widgetsShortcutsList.add(o);
                pInfo = new PackageItemInfo(packageName);
                pInfo = new PackageItemInfo(packageName);
                mIconCache.getTitleAndIconForApp(packageName, userHandle,
                mIconCache.getTitleAndIconForApp(packageName, userHandle,