Apply a new icon cache mechanism for memory improvement

- Avoid loading all app icons at once to decrease memory usage.
- Only load visible icons when entering the apps page.
- Reserve icon placeholder to alleviate icon loading flicker.
- Release icon cache when low memory or leaving apps page.

Bug: 187118427
Bug: 209898662
Test: manual check the smoothness and memory usage of apps pages.
Change-Id: Ifc3c2a73cc88d6e42739df4e8208445afa12e0ea
This commit is contained in:
Yanting Yang
2022-01-07 01:55:39 +08:00
parent 0128837312
commit 19bdc6ce67
6 changed files with 65 additions and 14 deletions

View File

@@ -586,4 +586,8 @@
<!-- The ratio to use when using the two-pane settings layout -->
<item name="config_activity_embed_split_ratio" format="float" type="dimen">0.5</item>
<!-- The number of visible app icons while entering app list related pages for preloading.
Take the "Unrestricted data" page as the example, the visible app icons could be 15
on 6.4 inches screen size whether the font size and display size are both small. -->
<integer name="config_num_visible_app_icons">20</integer>
</resources>

View File

@@ -20,6 +20,7 @@ import android.app.Application;
import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
import com.android.settings.homepage.SettingsHomepageActivity;
import com.android.settingslib.applications.AppIconCacheManager;
import java.lang.ref.WeakReference;
@@ -44,4 +45,10 @@ public class SettingsApplication extends Application {
public SettingsHomepageActivity getHomeActivity() {
return mHomeActivity.get();
}
@Override
public void onLowMemory() {
super.onLowMemory();
AppIconCacheManager.getInstance().release();
}
}

View File

@@ -43,6 +43,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -125,6 +126,8 @@ import com.android.settings.notification.app.AppNotificationSettings;
import com.android.settings.widget.LoadingViewController;
import com.android.settings.wifi.AppStateChangeWifiStateBridge;
import com.android.settings.wifi.ChangeWifiStateDetails;
import com.android.settingslib.applications.AppIconCacheManager;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
@@ -556,6 +559,7 @@ public class ManageApplications extends InstrumentedFragment
mApplications.release();
}
mRootView = null;
AppIconCacheManager.getInstance().release();
}
@Override
@@ -1323,6 +1327,11 @@ public class ManageApplications extends InstrumentedFragment
if (DEBUG) {
Log.d(TAG, "onRebuildComplete size=" + entries.size());
}
// Preload top visible icons of app list.
AppUtils.preloadTopIcons(mContext, entries,
mContext.getResources().getInteger(R.integer.config_num_visible_app_icons));
final int filterType = mAppFilter.getFilterType();
if (filterType == FILTER_APPS_POWER_ALLOWLIST
|| filterType == FILTER_APPS_POWER_ALLOWLIST_ALL) {
@@ -1480,8 +1489,7 @@ public class ManageApplications extends InstrumentedFragment
synchronized (entry) {
mState.ensureLabelDescription(entry);
holder.setTitle(entry.label, entry.labelDescription);
mState.ensureIcon(entry);
holder.setIcon(entry.icon);
updateIcon(holder, entry);
updateSummary(holder, entry);
updateSwitch(holder, entry);
holder.updateDisableView(entry.info);
@@ -1491,6 +1499,20 @@ public class ManageApplications extends InstrumentedFragment
holder.itemView.setOnClickListener(mManageApplications);
}
private void updateIcon(ApplicationViewHolder holder, AppEntry entry) {
final Drawable cachedIcon = AppUtils.getIconFromCache(entry);
if (cachedIcon != null && entry.mounted) {
holder.setIcon(cachedIcon);
} else {
ThreadUtils.postOnBackgroundThread(() -> {
final Drawable icon = AppUtils.getIcon(mContext, entry);
if (icon != null) {
ThreadUtils.postOnMainThread(() -> holder.setIcon(icon));
}
});
}
}
private void updateSummary(ApplicationViewHolder holder, AppEntry entry) {
switch (mManageApplications.mListType) {
case LIST_TYPE_NOTIFICATION:

View File

@@ -25,6 +25,7 @@ import android.view.View;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.applications.AppIconCacheManager;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
import com.android.settingslib.search.SearchIndexable;
@@ -112,6 +113,12 @@ public class UnrestrictedDataAccess extends DashboardFragment {
return R.xml.unrestricted_data_access_settings;
}
@Override
public void onDestroyView() {
super.onDestroyView();
AppIconCacheManager.getInstance().release();
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.unrestricted_data_access_settings);
}

View File

@@ -16,6 +16,7 @@ package com.android.settings.datausage;
import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfMeteredDataRestricted;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.view.View;
@@ -26,8 +27,10 @@ import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedPreferenceHelper;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.AppSwitchPreference;
public class UnrestrictedDataAccessPreference extends AppSwitchPreference implements
@@ -39,6 +42,7 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem
private final DataSaverBackend mDataSaverBackend;
private final DashboardFragment mParentFragment;
private final RestrictedPreferenceHelper mHelper;
private Drawable mCacheIcon;
public UnrestrictedDataAccessPreference(final Context context, AppEntry entry,
ApplicationsState applicationsState, DataSaverBackend dataSaverBackend,
@@ -56,8 +60,13 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem
UserHandle.getUserId(entry.info.uid)));
updateState();
setKey(generateKey(mEntry));
if (mEntry.icon != null) {
setIcon(mEntry.icon);
mCacheIcon = AppUtils.getIconFromCache(mEntry);
if (mCacheIcon != null) {
setIcon(mCacheIcon);
} else {
// Set empty icon as default.
setIcon(R.drawable.empty_icon);
}
}
@@ -101,16 +110,13 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
if (mEntry.icon == null) {
holder.itemView.post(new Runnable() {
@Override
public void run() {
// Ensure we have an icon before binding.
mApplicationsState.ensureIcon(mEntry);
// This might trigger us to bind again, but it gives an easy way to only
// load the icon once its needed, so its probably worth it.
setIcon(mEntry.icon);
}
if (mCacheIcon == null) {
ThreadUtils.postOnBackgroundThread(() -> {
final Drawable icon = AppUtils.getIcon(getContext(), mEntry);
ThreadUtils.postOnMainThread(() -> {
setIcon(icon);
mCacheIcon = icon;
});
});
}
final boolean disabledByAdmin = isDisabledByAdmin();

View File

@@ -29,6 +29,7 @@ import com.android.settings.applications.AppStateBaseBridge;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
@@ -125,6 +126,10 @@ public class UnrestrictedDataAccessPreferenceController extends BasePreferenceCo
return;
}
// Preload top visible icons of app list.
AppUtils.preloadTopIcons(mContext, apps,
mContext.getResources().getInteger(R.integer.config_num_visible_app_icons));
// Create apps key set for removing useless preferences
final Set<String> appsKeySet = new TreeSet<>();
// Add or update preferences