增加app列表页面,app数据存入数据库,优化icon cache

This commit is contained in:
2025-10-31 09:37:13 +08:00
parent 56341f6888
commit 5cc7caf34f
33 changed files with 1930 additions and 115 deletions

View File

@@ -1,4 +1,4 @@
package com.uiui.iconloader;
package com.ttstd.iconloader;
import android.content.Context;

View File

@@ -1 +1 @@
<manifest package="com.uiui.iconloader" />
<manifest package="com.ttstd.iconloader" />

View File

@@ -1,4 +1,4 @@
package com.uiui.iconloader;
package com.ttstd.iconloader;
import android.content.ComponentName;
import android.content.Intent;

View File

@@ -0,0 +1,168 @@
package com.ttstd.iconloader;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.util.Log;
import androidx.collection.LruCache;
import com.jakewharton.disklrucache.DiskLruCache;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class IconCacheManager {
private static IconCacheManager INSTANCE;
private static final String TAG = "IconCacheManager";
private LruCache<String, Drawable> mMemoryCache;
private DiskLruCache mDiskCache;
public static void init(Context context) {
if (context == null) {
throw new RuntimeException("Context is NULL");
}
if (INSTANCE == null) {
INSTANCE = new IconCacheManager(context);
}
}
public static IconCacheManager getInstance() {
if (INSTANCE == null) {
throw new IllegalStateException("You must be init IconCacheManager first");
}
return INSTANCE;
}
public IconCacheManager(Context context) {
// 内存缓存最大8MB
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Drawable>(cacheSize) {
@Override
protected int sizeOf(String key, Drawable icon) {
if (icon instanceof BitmapDrawable) {
return ((BitmapDrawable) icon).getBitmap().getByteCount() / 1024;
}
return 1; // 非Bitmap对象按1KB计算
}
};
// 磁盘缓存50MB
File cacheDir = new File(context.getExternalCacheDir(), "icon_cache");
try {
mDiskCache = DiskLruCache.open(cacheDir, 1, 1, 50 * 1024 * 1024);
} catch (Exception e) {
Log.e(TAG, "IconCacheManager: " + e.getMessage());
}
final PackageManager packageManager = context.getPackageManager();
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.execute(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
// 查询所有包含CATEGORY_LAUNCHER的Activity
List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL);
for (ResolveInfo resolveInfo : resolveInfoList) {
ComponentName componentName = new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
String key = componentName.flattenToShortString();
try {
ActivityInfo info = packageManager.getActivityInfo(componentName, 0);
Drawable rawIcon = info.loadIcon(packageManager);
putToMemory(key, rawIcon);
putToDisk(key, rawIcon);
} catch (Exception e) {
Log.e(TAG, "run: " + e.getMessage());
}
}
}
});
}
public Drawable getFromMemory(String key) {
return mMemoryCache.get(key);
}
public Drawable getFromDisk(String key) {
String md5 = MD5Util.getMD5(key);
try {
DiskLruCache.Snapshot snapshot = mDiskCache.get(md5);
if (snapshot != null) {
InputStream in = snapshot.getInputStream(0);
return Drawable.createFromStream(in, null);
}
} catch (IOException e) {
Log.e(TAG, "getFromDisk Disk read error", e);
}
return null;
}
public void putToMemory(String key, Drawable icon) {
mMemoryCache.put(key, icon);
}
public void putToDisk(String key, Drawable icon) {
String md5 = MD5Util.getMD5(key);
try {
DiskLruCache.Editor editor = mDiskCache.edit(md5);
if (icon instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) icon).getBitmap();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, editor.newOutputStream(0));
editor.commit();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (icon instanceof AdaptiveIconDrawable) {
// 分别获取背景和前景Drawable
Drawable[] layers = new Drawable[2];
layers[0] = ((AdaptiveIconDrawable) icon).getBackground();
layers[1] = ((AdaptiveIconDrawable) icon).getForeground();
LayerDrawable layerDrawable = new LayerDrawable(layers);
int width = icon.getIntrinsicWidth();
int height = icon.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
layerDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
layerDrawable.draw(canvas);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, editor.newOutputStream(0));
editor.commit();
}
}
} catch (IOException e) {
Log.e(TAG, "putToDisk Disk write error", e);
}
}
public Drawable getIcon(String key) {
Drawable memDrawable = getFromMemory(key);
if (memDrawable != null) {
Log.i(TAG, "getIcon: fromMemory " + key);
return memDrawable;
}
Drawable diskDrawable = getFromDisk(key);
if (diskDrawable != null) {
putToMemory(key, diskDrawable);
Log.i(TAG, "getIcon: fromDisk " + key);
return diskDrawable;
}
return null;
}
}

View File

@@ -1,4 +1,4 @@
package com.uiui.iconloader;
package com.ttstd.iconloader;
import android.content.ComponentName;
import android.content.Context;
@@ -12,29 +12,24 @@ import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import androidx.loader.content.AsyncTaskLoader;
public class IconLoader extends AsyncTaskLoader<Drawable> {
private final ComponentName mComponentName; // 关键使用ComponentName标识组件
private static final String TAG = "IconLoader";
private final IconCacheManager mCache;
private ComponentName mComponentName;
public IconLoader(Context context, ComponentName componentName, IconCacheManager cache) {
public IconLoader(Context context, ComponentName componentName, IconCacheManager iconCacheManager) {
super(context);
mComponentName = componentName;
mCache = cache;
this.mComponentName = componentName;
this.mCache = iconCacheManager;
}
@Override
protected void onStartLoading() {
String key = CacheKeyGenerator.generateKey(mComponentName);
// 1. 检查内存缓存
Drawable cachedIcon = mCache.getFromMemory(key);
if (cachedIcon != null) {
deliverResult(cachedIcon); // 直接返回缓存
return;
}
forceLoad(); // 触发后台加载
}
@Override
@@ -43,12 +38,15 @@ public class IconLoader extends AsyncTaskLoader<Drawable> {
// 1. 检查内存缓存
Drawable cachedIcon = mCache.getFromMemory(key);
if (cachedIcon != null) return cachedIcon;
if (cachedIcon != null) {
return cachedIcon;
}
// 2. 检查磁盘缓存
Drawable diskCached = mCache.getFromDisk(key);
if (diskCached != null) {
mCache.putToMemory(key, diskCached);
Log.e(TAG, "loadInBackground: putToMemory key = " + key);
return diskCached;
}
@@ -59,12 +57,12 @@ public class IconLoader extends AsyncTaskLoader<Drawable> {
Drawable rawIcon = info.loadIcon(pm); // 加载该组件的图标
// 4. 图标处理压缩/主题适配
Drawable processedIcon = processIcon(rawIcon);
// Drawable processedIcon = processIcon(rawIcon);
// 5. 更新缓存
mCache.putToMemory(key, processedIcon);
mCache.putToDisk(key, processedIcon);
return processedIcon;
mCache.putToMemory(key, rawIcon);
mCache.putToDisk(key, rawIcon);
return rawIcon;
} catch (PackageManager.NameNotFoundException e) {
return null;
}

View File

@@ -0,0 +1,29 @@
package com.ttstd.iconloader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Util {
public static String getMD5(String input) {
try {
// 1. 获取 MD5 消息摘要实例
MessageDigest md = MessageDigest.getInstance("MD5");
// 2. 将输入字符串转换为字节数组。建议指定编码如UTF-8但使用默认平台编码时通常可省略。
byte[] inputBytes = input.getBytes(); // 如需指定编码,可使用 input.getBytes("UTF-8")
// 3. 计算消息摘要得到128位16字节的哈希值
byte[] digest = md.digest(inputBytes);
// 4. 将字节数组转换为十六进制字符串表示
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
// 每个字节转换为两位十六进制数,不足两位前面补零
sb.append(String.format("%02x", b & 0xff));
}
// 5. 返回最终的32位十六进制字符串
return sb.toString();
} catch (NoSuchAlgorithmException e) {
// MD5算法是所有Java标准平台必须支持的此异常通常不会抛出
throw new RuntimeException("MD5 algorithm not available", e);
}
// 若指定了编码如UTF-8需捕获 UnsupportedEncodingException但UTF-8通常也受支持
}
}

View File

@@ -1,79 +0,0 @@
package com.uiui.iconloader;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import androidx.collection.LruCache;
import com.jakewharton.disklrucache.DiskLruCache;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public class IconCacheManager {
private static final String TAG = "IconCacheManager";
private LruCache<String, Drawable> mMemoryCache;
private DiskLruCache mDiskCache;
public IconCacheManager(Context context) {
// 内存缓存最大8MB
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Drawable>(cacheSize) {
@Override
protected int sizeOf(String key, Drawable icon) {
if (icon instanceof BitmapDrawable) {
return ((BitmapDrawable) icon).getBitmap().getByteCount() / 1024;
}
return 1; // 非Bitmap对象按1KB计算
}
};
// 磁盘缓存50MB
File cacheDir = new File(context.getCacheDir(), "icon_cache");
try {
mDiskCache = DiskLruCache.open(cacheDir, 1, 1, 50 * 1024 * 1024);
} catch (Exception e) {
Log.e(TAG, "IconCacheManager: " + e.getMessage());
}
}
public Drawable getFromMemory(String key) {
return mMemoryCache.get(key);
}
public Drawable getFromDisk(String key) {
try {
DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
if (snapshot != null) {
InputStream in = snapshot.getInputStream(0);
return Drawable.createFromStream(in, null);
}
} catch (IOException e) {
Log.e("IconCache", "Disk read error", e);
}
return null;
}
public void putToMemory(String key, Drawable icon) {
mMemoryCache.put(key, icon);
}
public void putToDisk(String key, Drawable icon) {
try {
DiskLruCache.Editor editor = mDiskCache.edit(key);
if (icon instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) icon).getBitmap();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, editor.newOutputStream(0));
editor.commit();
}
} catch (IOException e) {
Log.e("IconCache", "Disk write error", e);
}
}
}

View File

@@ -1,4 +1,4 @@
package com.uiui.iconloader;
package com.ttstd.iconloader;
import org.junit.Test;