增加app列表页面,app数据存入数据库,优化icon cache
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package com.uiui.iconloader;
|
||||
package com.ttstd.iconloader;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -1 +1 @@
|
||||
<manifest package="com.uiui.iconloader" />
|
||||
<manifest package="com.ttstd.iconloader" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.uiui.iconloader;
|
||||
package com.ttstd.iconloader;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
29
iconloader/src/main/java/com/ttstd/iconloader/MD5Util.java
Normal file
29
iconloader/src/main/java/com/ttstd/iconloader/MD5Util.java
Normal 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通常也受支持
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.uiui.iconloader;
|
||||
package com.ttstd.iconloader;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
Reference in New Issue
Block a user