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:
@@ -586,4 +586,8 @@
|
|||||||
<!-- The ratio to use when using the two-pane settings layout -->
|
<!-- 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>
|
<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>
|
</resources>
|
||||||
|
@@ -20,6 +20,7 @@ import android.app.Application;
|
|||||||
|
|
||||||
import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
|
import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
|
||||||
import com.android.settings.homepage.SettingsHomepageActivity;
|
import com.android.settings.homepage.SettingsHomepageActivity;
|
||||||
|
import com.android.settingslib.applications.AppIconCacheManager;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
@@ -44,4 +45,10 @@ public class SettingsApplication extends Application {
|
|||||||
public SettingsHomepageActivity getHomeActivity() {
|
public SettingsHomepageActivity getHomeActivity() {
|
||||||
return mHomeActivity.get();
|
return mHomeActivity.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLowMemory() {
|
||||||
|
super.onLowMemory();
|
||||||
|
AppIconCacheManager.getInstance().release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -43,6 +43,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageItemInfo;
|
import android.content.pm.PackageItemInfo;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
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.widget.LoadingViewController;
|
||||||
import com.android.settings.wifi.AppStateChangeWifiStateBridge;
|
import com.android.settings.wifi.AppStateChangeWifiStateBridge;
|
||||||
import com.android.settings.wifi.ChangeWifiStateDetails;
|
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;
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
||||||
@@ -556,6 +559,7 @@ public class ManageApplications extends InstrumentedFragment
|
|||||||
mApplications.release();
|
mApplications.release();
|
||||||
}
|
}
|
||||||
mRootView = null;
|
mRootView = null;
|
||||||
|
AppIconCacheManager.getInstance().release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1323,6 +1327,11 @@ public class ManageApplications extends InstrumentedFragment
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "onRebuildComplete size=" + entries.size());
|
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();
|
final int filterType = mAppFilter.getFilterType();
|
||||||
if (filterType == FILTER_APPS_POWER_ALLOWLIST
|
if (filterType == FILTER_APPS_POWER_ALLOWLIST
|
||||||
|| filterType == FILTER_APPS_POWER_ALLOWLIST_ALL) {
|
|| filterType == FILTER_APPS_POWER_ALLOWLIST_ALL) {
|
||||||
@@ -1480,8 +1489,7 @@ public class ManageApplications extends InstrumentedFragment
|
|||||||
synchronized (entry) {
|
synchronized (entry) {
|
||||||
mState.ensureLabelDescription(entry);
|
mState.ensureLabelDescription(entry);
|
||||||
holder.setTitle(entry.label, entry.labelDescription);
|
holder.setTitle(entry.label, entry.labelDescription);
|
||||||
mState.ensureIcon(entry);
|
updateIcon(holder, entry);
|
||||||
holder.setIcon(entry.icon);
|
|
||||||
updateSummary(holder, entry);
|
updateSummary(holder, entry);
|
||||||
updateSwitch(holder, entry);
|
updateSwitch(holder, entry);
|
||||||
holder.updateDisableView(entry.info);
|
holder.updateDisableView(entry.info);
|
||||||
@@ -1491,6 +1499,20 @@ public class ManageApplications extends InstrumentedFragment
|
|||||||
holder.itemView.setOnClickListener(mManageApplications);
|
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) {
|
private void updateSummary(ApplicationViewHolder holder, AppEntry entry) {
|
||||||
switch (mManageApplications.mListType) {
|
switch (mManageApplications.mListType) {
|
||||||
case LIST_TYPE_NOTIFICATION:
|
case LIST_TYPE_NOTIFICATION:
|
||||||
|
@@ -25,6 +25,7 @@ import android.view.View;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settings.search.BaseSearchIndexProvider;
|
import com.android.settings.search.BaseSearchIndexProvider;
|
||||||
|
import com.android.settingslib.applications.AppIconCacheManager;
|
||||||
import com.android.settingslib.applications.ApplicationsState;
|
import com.android.settingslib.applications.ApplicationsState;
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
||||||
import com.android.settingslib.search.SearchIndexable;
|
import com.android.settingslib.search.SearchIndexable;
|
||||||
@@ -112,6 +113,12 @@ public class UnrestrictedDataAccess extends DashboardFragment {
|
|||||||
return R.xml.unrestricted_data_access_settings;
|
return R.xml.unrestricted_data_access_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
AppIconCacheManager.getInstance().release();
|
||||||
|
}
|
||||||
|
|
||||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||||
new BaseSearchIndexProvider(R.xml.unrestricted_data_access_settings);
|
new BaseSearchIndexProvider(R.xml.unrestricted_data_access_settings);
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ package com.android.settings.datausage;
|
|||||||
import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfMeteredDataRestricted;
|
import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfMeteredDataRestricted;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
@@ -26,8 +27,10 @@ import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
|
|||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||||
import com.android.settingslib.RestrictedPreferenceHelper;
|
import com.android.settingslib.RestrictedPreferenceHelper;
|
||||||
|
import com.android.settingslib.applications.AppUtils;
|
||||||
import com.android.settingslib.applications.ApplicationsState;
|
import com.android.settingslib.applications.ApplicationsState;
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||||
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
import com.android.settingslib.widget.AppSwitchPreference;
|
import com.android.settingslib.widget.AppSwitchPreference;
|
||||||
|
|
||||||
public class UnrestrictedDataAccessPreference extends AppSwitchPreference implements
|
public class UnrestrictedDataAccessPreference extends AppSwitchPreference implements
|
||||||
@@ -39,6 +42,7 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem
|
|||||||
private final DataSaverBackend mDataSaverBackend;
|
private final DataSaverBackend mDataSaverBackend;
|
||||||
private final DashboardFragment mParentFragment;
|
private final DashboardFragment mParentFragment;
|
||||||
private final RestrictedPreferenceHelper mHelper;
|
private final RestrictedPreferenceHelper mHelper;
|
||||||
|
private Drawable mCacheIcon;
|
||||||
|
|
||||||
public UnrestrictedDataAccessPreference(final Context context, AppEntry entry,
|
public UnrestrictedDataAccessPreference(final Context context, AppEntry entry,
|
||||||
ApplicationsState applicationsState, DataSaverBackend dataSaverBackend,
|
ApplicationsState applicationsState, DataSaverBackend dataSaverBackend,
|
||||||
@@ -56,8 +60,13 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem
|
|||||||
UserHandle.getUserId(entry.info.uid)));
|
UserHandle.getUserId(entry.info.uid)));
|
||||||
updateState();
|
updateState();
|
||||||
setKey(generateKey(mEntry));
|
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
|
@Override
|
||||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||||
if (mEntry.icon == null) {
|
if (mCacheIcon == null) {
|
||||||
holder.itemView.post(new Runnable() {
|
ThreadUtils.postOnBackgroundThread(() -> {
|
||||||
@Override
|
final Drawable icon = AppUtils.getIcon(getContext(), mEntry);
|
||||||
public void run() {
|
ThreadUtils.postOnMainThread(() -> {
|
||||||
// Ensure we have an icon before binding.
|
setIcon(icon);
|
||||||
mApplicationsState.ensureIcon(mEntry);
|
mCacheIcon = icon;
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
final boolean disabledByAdmin = isDisabledByAdmin();
|
final boolean disabledByAdmin = isDisabledByAdmin();
|
||||||
|
@@ -29,6 +29,7 @@ import com.android.settings.applications.AppStateBaseBridge;
|
|||||||
import com.android.settings.core.BasePreferenceController;
|
import com.android.settings.core.BasePreferenceController;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settingslib.applications.AppUtils;
|
||||||
import com.android.settingslib.applications.ApplicationsState;
|
import com.android.settingslib.applications.ApplicationsState;
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
||||||
@@ -125,6 +126,10 @@ public class UnrestrictedDataAccessPreferenceController extends BasePreferenceCo
|
|||||||
return;
|
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
|
// Create apps key set for removing useless preferences
|
||||||
final Set<String> appsKeySet = new TreeSet<>();
|
final Set<String> appsKeySet = new TreeSet<>();
|
||||||
// Add or update preferences
|
// Add or update preferences
|
||||||
|
Reference in New Issue
Block a user