图片缓存

使用内存缓存
使用内存缓存可以快速的拿到bitmap,但是是以占用应用可用内存为代价的。 LruCache类就是为了实现bitmap的缓存的,它把最近被引用的对象存储在了一个强引用类型的LinkedHashMap中,在缓存大小超过它设定的值的时候就会赶出尽量少的最近使用的对象。
注意:在以前,一个比较受欢迎的内存缓存实现是SoftReference或者WeakReference bitmap缓存,然而并不建议这么做。从Android2.3(API 9)开始,android虚拟机的垃圾收集器变得更加的具有侵略性,极有可能会回收掉soft/weak的引用,这可能就会使使用soft/weak的缓存策略失效。而且,在android3.0(API 11)之前,bitmap的数据时存储在本地内存中的,这种内存的释放是不可预期的不可控的,这样就有可能造成内存达到阀值从而崩溃。
为了选择LruCache的合适大小,下面是几个需要考虑的因素:
-有多少图片会一次性的展示在屏幕上?有多少需要在稍后被还原到屏幕上?
-设备的屏幕大小的密度。xhdpi的密度比hdpi的设备需要更多的缓存空间大小。
-图片的尺寸和配置信息,每张图片会占用多少内存
-图片被使用的频率。是否有其中一些图片比其他的图片使用频率更高?如果这样的话,你可能要把这些图片始终放在内存中,甚至为不同的图片组使用多个LruCache对象。
-你能平衡好数量和质量么?有的时候可能存储大量的低质量的图片更加有用,而在后台默默的加载对应图片的高质量版本。
没有适合于所有应用的指定大小或者计算公式。全都依靠你自己的应用的内存使用情况,并自己给出合适的方案。太小不会有明显的效果,太大可能会导致OutOfMemory异常。
下面是一个例子:
private LruCache<String, Bitmap> mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
   
...
   
// Get max available VM memory, exceeding this amount will throw an
   
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
   
// int in its constructor.这里除以1024是因为maxMemory()方法返回的是字节数,而LruCache的构造函数是以KB作为最大值的单位
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

   
// Use 1/8th of the available memory for this memory cache.
   
final int cacheSize = maxMemory / 8;

    mMemoryCache
= new LruCache<String, Bitmap>(cacheSize) {
       
@Override
       
protected int sizeOf(String key, Bitmap bitmap) {
           
// The cache size will be measured in kilobytes rather than
           
// number of items.
           
return bitmap.getByteCount() / 1024;
       
}
   
};
   
...
}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
   
if (getBitmapFromMemCache(key) == null) {
        mMemoryCache
.put(key, bitmap);
   
}
}

public Bitmap getBitmapFromMemCache(String key) {
   
return mMemoryCache.get(key);
}
注意:在这个例子中,八分之一的应用内存分配给了我们的缓存。在hdpi的设备上,这大概是4MB左右的大小。一个480*800设备上的一个全屏的铺满图片的GridView大概会使用1.5(480*800*4 bytes)MB,所以这个能够缓存大概2.5页图片在内存中。
当向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;
   
}
   
...
}
使用磁盘缓存
像GridView这样的拥有大量数据的组件可能很容易就会填满内存缓存。你的应用也有可能被来电打断,或者在后台运行的时候被kill掉,这个时候如果用户重新进入到应用中,你的应用还要重新处理每一个图片。
磁盘缓存这时候就有用了,它可以帮助减少图片加载的次数(先到内存中找有没有有缓存,没有的话再到磁盘中找,再没有的话则请求网络数据)。当然,从磁盘中加载数据比从内存中加载要慢,并且要在后台线程中进行,因为磁盘的读取时间是不可预期的。
注意:如果缓存的图片频繁的被访问,ContentProvider可能是一个储存缓存图片的更好的选择,例如图片浏览器(Image Gallery)应用。
下面的实例代码是从android源码中抽取的一段DiskLruCache的代码。这里除了已经存在的内存缓存之外,还添加了一个磁盘缓存:
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
       
while (mDiskCacheStarting) {
           
try {
                mDiskCacheLock
.wait();
           
} catch (InterruptedException e) {}
       
}
       
if (mDiskLruCache != null) {
           
return mDiskLruCache.get(key);
       
}
   
}
   
return null;
}

// Creates a unique subdirectory of the designated app cache directory. Tries to use external
// but if not mounted, falls back on internal storage.
public static File getDiskCacheDir(Context context, String uniqueName) {
   
// Check if media is mounted or storage is built-in, if so, try and use external cache dir
   
// otherwise use internal cache dir
   
final String cachePath =
           
Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
                   
!isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
                            context
.getCacheDir().getPath();

   
return new File(cachePath + File.separator + uniqueName);
}



















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Verilog是一种硬件描述语言,用于设计和描述数字电路。图片缓存通常是在图形处理单元(GPU)中使用的一种技术,用于临时存储图像数据以提高图像处理效率。在Verilog中实现图片缓存涉及到处理图像数据的存储、读取和处理等方面。 在Verilog中,可以使用多种方式来实现图片缓存。一种常见的方式是使用双端口的RAM(Random Access Memory)来存储图像数据。其中一个端口用于写入图像数据,另一个端口用于读取图像数据。这样可以实现并行地读取和写入图像数据,提高数据访问的效率。 以下是一个简单示例,演示如何使用Verilog实现一个基本的图片缓存: ```verilog module ImageCache ( input wire [7:0] image_data, input wire write_enable, input wire read_enable, input wire [7:0] read_address, output wire [7:0] read_data ); reg [7:0] cache [0:255]; always @(posedge clk) begin if (write_enable) begin cache[read_address] <= image_data; end end always @(posedge clk) begin if (read_enable) begin read_data <= cache[read_address]; end end endmodule ``` 上述代码定义了一个名为ImageCache的模块,其中包含一个8位宽的图像数据输入端口(image_data)、一个写使能端口(write_enable)、一个读使能端口(read_enable)、一个8位宽的读地址端口(read_address)和一个8位宽的读数据输出端口(read_data)。 在模块内部,使用了一个由256个8位宽寄存器(cache)组成的数组来实现图片缓存。在上升沿时钟触发的always块中,根据写使能信号,将图像数据写入到指定的缓存地址中;在下降沿时钟触发的always块中,根据读使能信号和读地址,从指定的缓存地址读取图像数据并输出到读数据端口。 需要注意的是,上述示例只是一个简单的图片缓存实现,实际应用中可能需要考虑更多的功能和细节,例如数据读写的时序、地址映射算法、缓存策略等。具体的实现方式和细节可以根据具体需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值