Android 缓存Bitmaps(2)

本文探讨了在Android应用中合理设置LruCache大小的方法,考虑因素包括内存消耗、屏幕显示图片数量、设备特性等。作者提供了内存和硬盘缓存的实例代码,强调了缓存策略与内存管理的重要性,以及在实际场景下的灵活调整和使用硬盘缓存作为备份的必要性。
摘要由CSDN通过智能技术生成

为了给LruCache选择一个合适的大小,要考虑到很多原因,例如:

• 其他的Activity(活动)和(或)程序都是很耗费内存的吗?

• 屏幕上一次会显示多少图片?有多少图片将在屏幕上显示?

• 设备的屏幕大小和密度是多少?一个超高清屏幕(xhdpi)的设备如Galaxy Nexus,相比Nexus S(hdpi)来说,缓存同样数量的图片需要更大的缓存空间。

• Bitmap的尺寸、配置以及每张图片需要占用多少内存?

• 图片的访问是否频繁?有些会比其他的更加被频繁的访问到吗?如果是这样,也许你需要将某些图片一直保留在内存中,甚至需要多个LruCache对象分配给不同组的Bitmap。

• 你能平衡图片的质量和数量么?有的时候存储大量低质量的图片更加有用,然后可以在后台任务中加载另一个高质量版本的图片。

对于设置缓存大小,并没有适用于所有应用的规范,它取决于你在内存使用分析后给出的合适的解决方案。缓存空间太小并无益处,反而会引起额外的开销,而太大了又可能再次引起java.lang.OutOfMemory异常或只留下很小的空间给应用的其他程序运行。

这里有一个设置Bitmap的LruCache示例:

private LruCache<String, Bitmap> mMemoryCache;

@Override

protected void onCreate(Bundle savedInstanceState) {

// Get memory class of this device, exceeding this amount will throw an

// OutOfMemory exception.

final int memClass = ((ActivityManager) context.getSystemService(

Context.ACTIVITY_SERVICE)).getMemoryClass();

// Use 1/8th of the available memory for this memory cache.

final int cacheSize = 1024 * 1024 * memClass / 8;

mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {

@Override

protected int sizeOf(String key, Bitmap bitmap) {

// The cache size will be measured in bytes rather than number of items.

return bitmap.getByteCount();

}

};

}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {

if (getBitmapFromMemCache(key) == null) {

mMemoryCache.put(key, bitmap);

}

}

public Bitmap getBitmapFromMemCache(String key) {

return mMemoryCache.get(key);

}

注意:在这个例子中,1/8的应用内存被分配给缓存。在一个普通的/hdpi设备上最低也在4M左右(32/8)。一个分辨率为800*480的设备上,全屏的填满图片的GridView占用的内存约1.5M(800*480*4字节),因此这个大小的内存可以缓存2.5页左右的图片。

当加载一个Bitmap到ImageView中,先要检查LruCache。如果有相应的数据,则立即用来更新ImageView,否则将启动后台线程来处理这个图片。

public void loadBitmap(int resId, ImageView imageView) {

final String imageKey = String.valueOf(resId);

final Bitmap bitmap = getBitmapFromMemCache(imageKey);

if (bitmap != null) {

mImageView.setImageBitmap(bitmap);

} else {

mImageView.setImageResource(R.drawable.image_placeholder);

BitmapWorkerTask task = new BitmapWorkerTask(mImageView);

task.execute(resId);

}

}

BitmapWorkerTask也需要更新内存中的数据:

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {

// Decode image in background.

@Override

protected Bitmap doInBackground(Integer… params) {

final Bitmap bitmap = decodeSampledBitmapFromResource(

getResources(), params[0], 100, 100));

addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);

return bitmap;

}

}

使用硬盘缓存

一个内存缓存对加速访问最近浏览过的Bitmap非常有帮助,但是你不能局限于内存中的可用图片。GridView这样有着更大的数据集的组件可以很轻易消耗掉内存缓存。你的应用有可能在执行其他任务(如打电话)的时候被打断,并且在后台的任务有可能被杀死或者缓存被释放。一旦用户重新聚焦(resume)到你的应用,你得再次处理每一张图片。

在这种情况下,硬盘缓存可以用来存储Bitmap并在图片被内存缓存释放后减小图片加载的时间(次数)。当然,从硬盘加载图片比内存要慢,并且应该在后台线程进行,因为硬盘读取的时间是不可预知的。

注意:如果访问图片的次数非常频繁,那么ContentProvider可能更适合用来存储缓存图片,例如Image Gallery这样的应用程序。

这个类中的示例代码使用DiskLruCache(来自Android源码)实现。在示例代码中,除了已有的内存缓存,还添加了硬盘缓存。

private DiskLruCache mDiskLruCache;

private final Object mDiskCacheLock = new Object();

private boolean mDiskCacheStarting = true;

private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB

private static final String DISK_CACHE_SUBDIR = “thumbnails”;

@Override

protected void onCreate(Bundle savedInstanceState) {

// Initialize memory cache

// Initialize disk cache on background thread

File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);

new InitDiskCacheTask().execute(cacheDir);

}

class InitDiskCacheTask extends AsyncTask<File, Void, Void> {

@Override

protected Void doInBackground(File… params) {

synchronized (mDiskCacheLock) {

File cacheDir = params[0];

mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);

mDiskCacheStarting = false; // Finished initialization

mDiskCacheLock.notifyAll(); // Wake any waiting threads

}

return null;

}

}

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {

// Decode image in background.

@Override

protected Bitmap doInBackground(Integer… params) {

final String imageKey = String.valueOf(params[0]);

// Check disk cache in background thread

Bitmap bitmap = getBitmapFromDiskCache(imageKey);

if (bitmap == null) { // Not found in disk cache

// Process as normal

final Bitmap bitmap = decodeSampledBitmapFromResource(

getResources(), params[0], 100, 100));

}

// Add final bitmap to caches

addBitmapToCache(imageKey, bitmap);

return bitmap;

}

}

public void addBitmapToCache(String key, Bitmap bitmap) {

// Add to memory cache as before

if (getBitmapFromMemCache(key) == null) {

mMemoryCache.put(key, bitmap);

}

// Also add to disk cache

synchronized (mDiskCacheLock) {

if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {

mDiskLruCache.put(key, bitmap);

}

}

}

public Bitmap getBitmapFromDiskCache(String key) {

synchronized (mDiskCacheLock) {

// Wait while disk cache is started from background thread
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。

image

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

2024/03/13/H4lCoPEF.jpg" />

最后

写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。

[外链图片转存中…(img-Wde52eBa-1712687567687)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值