From eea022530496bb5f53f9ae01fdef0cd9d0e8147d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 27 Jul 2015 17:50:13 -0700 Subject: 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 --- .../model/WidgetsAndShortcutNameComparator.java | 85 +++++++++++++++------- src/com/android/launcher3/model/WidgetsModel.java | 5 +- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java index 61e895283..b99056023 100644 --- a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java +++ b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java @@ -1,13 +1,14 @@ package com.android.launcher3.model; +import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; - import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Utilities; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.util.ComponentKey; import java.text.Collator; import java.util.Comparator; @@ -16,53 +17,81 @@ import java.util.HashMap; public class WidgetsAndShortcutNameComparator implements Comparator { private final AppWidgetManagerCompat mManager; private final PackageManager mPackageManager; - private final HashMap mLabelCache; + private final HashMap mLabelCache; private final Collator mCollator; private final UserHandleCompat mMainHandle; public WidgetsAndShortcutNameComparator(Context context) { mManager = AppWidgetManagerCompat.getInstance(context); mPackageManager = context.getPackageManager(); - mLabelCache = new HashMap(); + mLabelCache = new HashMap<>(); mCollator = Collator.getInstance(); mMainHandle = UserHandleCompat.myUserHandle(); } - @Override - public final int compare(Object a, Object b) { - String labelA, labelB; - if (mLabelCache.containsKey(a)) { - labelA = mLabelCache.get(a); - } 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. + /** + * Resets any stored state. + */ + public void reset() { + mLabelCache.clear(); + } - boolean aWorkProfile = (a instanceof LauncherAppWidgetProviderInfo) && - !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) a)); - boolean bWorkProfile = (b instanceof LauncherAppWidgetProviderInfo) && - !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) b)); + @Override + public final int compare(Object objA, Object objB) { + ComponentKey keyA = getComponentKey(objA); + ComponentKey keyB = getComponentKey(objB); // Independent of how the labels compare, if only one of the two widget info belongs to // work profile, put that one in the back. + boolean aWorkProfile = !mMainHandle.equals(keyA.user); + boolean bWorkProfile = !mMainHandle.equals(keyB.user); if (aWorkProfile && !bWorkProfile) { return 1; } if (!aWorkProfile && bWorkProfile) { 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 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)); + } + } }; diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java index 185dfcae3..cabff148f 100644 --- a/src/com/android/launcher3/model/WidgetsModel.java +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -39,7 +39,7 @@ public class WidgetsModel { private ArrayList mRawList; private final AppWidgetManagerCompat mAppWidgetMgr; - private final Comparator mWidgetAndShortcutNameComparator; + private final WidgetsAndShortcutNameComparator mWidgetAndShortcutNameComparator; private final Comparator mAppNameComparator; private final IconCache mIconCache; private final AppFilter mAppFilter; @@ -103,6 +103,7 @@ public class WidgetsModel { // clear the lists. mWidgetsList.clear(); mPackageItemInfos.clear(); + mWidgetAndShortcutNameComparator.reset(); // add and update. for (Object o: rawWidgetsShortcuts) { @@ -139,7 +140,7 @@ public class WidgetsModel { if (widgetsShortcutsList != null) { widgetsShortcutsList.add(o); } else { - widgetsShortcutsList = new ArrayList(); + widgetsShortcutsList = new ArrayList<>(); widgetsShortcutsList.add(o); pInfo = new PackageItemInfo(packageName); mIconCache.getTitleAndIconForApp(packageName, userHandle, -- cgit v1.2.3