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

Commit 19bdc6ce authored by Yanting Yang's avatar Yanting Yang
Browse files

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
parent 01288373
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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>
+7 −0
Original line number Diff line number Diff line
@@ -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();
    }
}
+24 −2
Original line number Diff line number Diff line
@@ -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:
+7 −0
Original line number Diff line number Diff line
@@ -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);
}
+18 −12
Original line number Diff line number Diff line
@@ -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();
Loading