LruCache缓存

article/2025/8/25 4:18:06

Lru算法: Lru 指的是“Least Recently Used-近期最少使用算法”。
1、那么LruCache到底是什么呢?
LruCache 是对限定数量的缓存对象持有强引用的缓存,每一次缓存对象被访问,都会被移动到队列的头部。当有对象要被添加到已经达到数量上限的 LruCache 中,队列尾部的对象将会被移除,而且可能会被垃圾回收器回收。LruCache 中的 Lru 指的是“Least Recently Used-近期最少使用算法”。这就意味着,LruCache 是一个能够判断哪个缓存对象是近期最少使用的缓存对象,从而把最少使用的移至队尾,而近期使用的则留在队列前面了。举个例子:比如我们有a、b、c、d、e五个元素,而a、c、d、e都被访问过,唯有b元素没有访问,则b元素就成为了近期最少使用元素了,就移至在队尾了。

从上面的叙述中我们总结可以知道LruCache核心思想就两点:

1、LruCache使用的是近期最少使用算法,近期使用最少的将会移至到队尾,而近期刚刚使用的则会移至到头部。

2、LruCache缓存的大小是限定的(限定的大小由我们定义),当LruCache存储空间满了就会移除队尾的而为新的对象的加入腾出控件。

2、我们还是从源码入手学学LruCache到底怎么使用吧:
在这里插入图片描述
显示发现一堆int类型的变量,还有一个最重要的LinkedHashMap<K,V> 这个队列,通俗的讲LinkedHashMap<K,V>就是一个双向链表存储结构。

各个变量的意思为:

size - LruCache中已经存储的大小

maxSize - 我们定义的LruCache缓存最大的空间

putCount - put的次数(为LruCache添加缓存对象的次数)

createCount - create的次数

evictionCount - 回收的次数

hitCount - 命中的次数

missCount - 丢失的次数

再看看构造器:

public LruCache(int maxSize) {if (maxSize <= 0) {throw new IllegalArgumentException("maxSize <= 0");}this.maxSize = maxSize;this.map = new LinkedHashMap<K, V>(0, 0.75f, true);}

发现需要传入一个int类型的值,顾名思义,这就是我们定义的LruCache缓存的空间大小了,一般情况下我们可以得到应用程序的最大可用空间,然后按百分比取值设置给它即可。
再看看其它一些比较重要的方法:

put()方法:

 public final V put(K key, V value) {if (key == null || value == null) {throw new NullPointerException("key == null || value == null");}V previous;synchronized (this) {putCount++;size += safeSizeOf(key, value);previous = map.put(key, value);if (previous != null) {size -= safeSizeOf(key, previous);}}if (previous != null) {entryRemoved(false, key, previous, value);}trimToSize(maxSize);return previous;}

通过该方法我们可以知道LruCache中是通过<Key,Value>形式存储缓存数据的。意思就是我们把一个Value存储到LruCache中,并设置对应键值为key。然后判断key和value都不能为空,否则就抛异常了。之后把该Value移至队列的头部。
get()方法:

  public final V get(K key) {if (key == null) {throw new NullPointerException("key == null");}V mapValue;synchronized (this) {mapValue = map.get(key);if (mapValue != null) {hitCount++;return mapValue;}missCount++;}/** Attempt to create a value. This may take a long time, and the map* may be different when create() returns. If a conflicting value was* added to the map while create() was working, we leave that value in* the map and release the created value.*/V createdValue = create(key);if (createdValue == null) {return null;}synchronized (this) {createCount++;mapValue = map.put(key, createdValue);if (mapValue != null) {// There was a conflict so undo that last putmap.put(key, mapValue);} else {size += safeSizeOf(key, createdValue);}}if (mapValue != null) {entryRemoved(false, key, createdValue, mapValue);return mapValue;} else {trimToSize(maxSize);return createdValue;}}

该方法就是得到对应key缓存的Value,假如该Value存在,返回Value并且移至该Value至队列的头部,这也证实了最近最先使用的将会移至队列的头部。假如Value不存在则返回null。
remove()方法:

public final V remove(K key) {
if (key == null) {
throw new NullPointerException(“key == null”);
}

V previous;
synchronized (this) {previous = map.remove(key);if (previous != null) {size -= safeSizeOf(key, previous);}
}if (previous != null) {entryRemoved(false, key, previous, null);
}return previous;

}
该方法就是从LruCache缓存中移除对应key的Value值。
sizeof()方法:一般需要重写的:

 protected int sizeOf(K key, V value) {return 1;}

重写它计算不同的Value的大小。一般我们会这样重写:

mLruCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {if(bitmap!=null){return bitmap.getByteCount();}return 0;}};

好了,总结一下使用LruCache的原理:比如像ImageView中加载一张图片时候,首先会在LruCache的缓存中检查是否有对应的key值(get( key)),如果有就返回对应的Bitmap,从而更新ImageView,如果没有则重新开启一个异步线程来重新加载这张图片。

来看看用LruCache缓存Bitmap的例子:

public class MyLruCache extends AppCompatActivity{private LruCache<String,Bitmap> mLruCache;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//得到应用程序最大可用内存int maxCache = (int) Runtime.getRuntime().maxMemory();int cacheSize = maxCache / 8;//设置图片缓存大小为应用程序总内存的1/8mLruCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {if(bitmap!=null){return bitmap.getByteCount();}return 0;}};}/*** 添加Bitmap到LruCache中** @param key* @param bitmap*/public void putBitmapToLruCache(String key, Bitmap bitmap) {if (getBitmapFromLruCache(key) == null) {mLruCache.put(key, bitmap);}}/*** @param key* @return 从LruCache缓存中获取一张Bitmap,没有则会返回null*/public Bitmap getBitmapFromLruCache(String key) {return mLruCache.get(key);}
}

贴下主要代码:

MainActivity:

public class MainActivity extends ActionBarActivity {private GridView mGridView;private List<String> datas;private Toolbar mToolbar;private GridViewAdapter mAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.v("zxy", "cache:" + getCacheDir().getPath());Log.v("zxy", "Excache:" + getExternalCacheDir().getPath());mToolbar = (Toolbar) findViewById(R.id.toolbar);mToolbar.setTitleTextColor(Color.WHITE);mToolbar.setNavigationIcon(R.mipmap.icon);setSupportActionBar(mToolbar);initDatas();mGridView = (GridView) findViewById(R.id.gridView);mAdapter = new GridViewAdapter(this, mGridView, datas);mGridView.setAdapter(mAdapter);mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {Toast.makeText(MainActivity.this, "position=" + position + ",id=" + id, Toast.LENGTH_SHORT).show();}});}public void initDatas() {datas = new ArrayList<>();for (int i = 0; i < 55; i++) {datas.add(URLDatasTools.imageUrls[i]);}}@Overrideprotected void onDestroy() {super.onDestroy();mAdapter.cancelAllDownloadTask();//取消所有下载任务}
}

GridViewAdapter:

public class GridViewAdapter extends BaseAdapter implements AbsListView.OnScrollListener {private List<DownloadTask> mDownloadTaskList;//所有下载异步线程的集合private Context mContext;private GridView mGridView;private List<String> datas;private LruCache<String, Bitmap> mLruCache;private int mFirstVisibleItem;//当前页显示的第一个item的位置positionprivate int mVisibleItemCount;//当前页共显示了多少个itemprivate boolean isFirstRunning = true;public GridViewAdapter(Context context, GridView mGridView, List<String> datas) {this.mContext = context;this.datas = datas;this.mGridView = mGridView;this.mGridView.setOnScrollListener(this);mDownloadTaskList = new ArrayList<>();initCache();}private void initCache() {//得到应用程序最大可用内存int maxCache = (int) Runtime.getRuntime().maxMemory();int cacheSize = maxCache / 8;//设置图片缓存大小为应用程序总内存的1/8mLruCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {if (bitmap != null) {return bitmap.getByteCount();}return 0;}};}@Overridepublic int getCount() {return datas.size();}@Overridepublic Object getItem(int position) {return datas.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {convertView = LayoutInflater.from(mContext).inflate(R.layout.layout_item, parent, false);ImageView mImageView = (ImageView) convertView.findViewById(R.id.imageView);TextView mTextView = (TextView) convertView.findViewById(R.id.textView);String url = datas.get(position);mImageView.setTag(String2MD5Tools.hashKeyForDisk(url));//设置一个Tag为md5(url),保证图片不错乱显示mTextView.setText("第" + position + "项");setImageViewForBitmap(mImageView, url);return convertView;}/*** 给ImageView设置Bitmap** @param imageView* @param url*/private void setImageViewForBitmap(ImageView imageView, String url) {String key = String2MD5Tools.hashKeyForDisk(url);//对url进行md5编码Bitmap bitmap = getBitmapFromLruCache(key);if (bitmap != null) {//如果缓存中存在,那么就设置缓存中的bitmapimageView.setImageBitmap(bitmap);} else {//不存在就设置个默认的背景色imageView.setBackgroundResource(R.color.color_five);}}/*** 添加Bitmap到LruCache中** @param key* @param bitmap*/public void putBitmapToLruCache(String key, Bitmap bitmap) {if (getBitmapFromLruCache(key) == null) {mLruCache.put(key, bitmap);}}/*** @param key* @return 从LruCache缓存中获取一张Bitmap,没有则会返回null*/public Bitmap getBitmapFromLruCache(String key) {return mLruCache.get(key);}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if (scrollState == SCROLL_STATE_IDLE) {//GridView为静止状态时,让它去下载图片loadBitmap(mFirstVisibleItem, mVisibleItemCount);} else {//滚动时候取消所有下载任务cancelAllDownloadTask();}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {mFirstVisibleItem = firstVisibleItem;mVisibleItemCount = visibleItemCount;if (isFirstRunning && visibleItemCount > 0) {//首次进入时加载图片loadBitmap(mFirstVisibleItem, mVisibleItemCount);isFirstRunning = false;}}/*** 加载图片到ImageView中** @param mFirstVisibleItem* @param mVisibleItemCount*/private void loadBitmap(int mFirstVisibleItem, int mVisibleItemCount) {//首先判断图片在不在缓存中,如果不在就开启异步线程去下载该图片for (int i = mFirstVisibleItem; i < mFirstVisibleItem + mVisibleItemCount; i++) {final String url = datas.get(i);String key = String2MD5Tools.hashKeyForDisk(url);Bitmap bitmap = getBitmapFromLruCache(key);if (bitmap != null) {//缓存中存在该图片的话就设置给ImageViewImageView mImageView = (ImageView) mGridView.findViewWithTag(String2MD5Tools.hashKeyForDisk(url));if (mImageView != null) {mImageView.setImageBitmap(bitmap);}} else {//不存在的话就开启一个异步线程去下载DownloadTask task = new DownloadTask();mDownloadTaskList.add(task);//把下载任务添加至下载集合中task.execute(url);}}}class DownloadTask extends AsyncTask<String, Void, Bitmap> {String url;@Overrideprotected Bitmap doInBackground(String... params) {//在后台开始下载图片url = params[0];Bitmap bitmap = downloadBitmap(url);if (bitmap != null) {//把下载好的图片放入LruCache中String key = String2MD5Tools.hashKeyForDisk(url);putBitmapToLruCache(key, bitmap);}return bitmap;}@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);//把下载好的图片显示出来ImageView mImageView = (ImageView) mGridView.findViewWithTag(String2MD5Tools.hashKeyForDisk(url));if (mImageView != null && bitmap != null) {mImageView.setImageBitmap(bitmap);mDownloadTaskList.remove(this);//把下载好的任务移除}}}/*** @param tasks* 取消所有的下载任务*/public void cancelAllDownloadTask(){if(mDownloadTaskList!=null){for (int i = 0; i < mDownloadTaskList.size(); i++) {mDownloadTaskList.get(i).cancel(true);}}}/*** 建立网络链接下载图片** @param urlStr* @return*/private Bitmap downloadBitmap(String urlStr) {HttpURLConnection connection = null;Bitmap bitmap = null;try {URL url = new URL(urlStr);connection = (HttpURLConnection) url.openConnection();connection.setConnectTimeout(5000);connection.setReadTimeout(5000);connection.setDoInput(true);connection.connect();if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {InputStream mInputStream = connection.getInputStream();bitmap = BitmapFactory.decodeStream(mInputStream);}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (connection != null) {connection.disconnect();}}return bitmap;}

}


http://chatgpt.dhexx.cn/article/w5YETbjV.shtml

相关文章

LruCache 源码解析

1. 概述 对于 Android 开发者&#xff0c;LruCache 肯定不陌生&#xff0c;几乎所有的图片缓存框架都会用到它来实现内存缓存等&#xff0c;可见 LruCache 在 Android 开发中的重要性。LRU 是 Least Recently Used 的缩写&#xff0c;近期最少使用的意思。当我们进行缓存的时候…

LruCache

LruCache这个类是通过Glide得知的&#xff0c;不过它是自己又基于LRU算法自己写了个LruCache工具类&#xff0c;不过基本原理类似&#xff0c;都是基于LRU算法实现的 1.来源 一般来说&#xff0c;缓存策略主要包含缓存的添加、获取和删除这三类操作。如何添加和获取缓存这个比…

Android基础-LruCache原理解析

一、Android中的缓存策略 一般来说&#xff0c;缓存策略主要包含缓存的添加、获取和删除这三类操作。如何添加和获取缓存这个比较好理解&#xff0c;那么为什么还要删除缓存呢&#xff1f;这是因为不管是内存缓存还是硬盘缓存&#xff0c;它们的缓存大小都是有限的。当缓存满了…

LRUCache详解

1.概念 LRU是Least Recently Used的缩写&#xff0c;意思是最近最少使用&#xff0c;它是一种Cache替换算法。 Cache的容量有限&#xff0c;因此当Cache的容量用完后&#xff0c;而又有新的内容需要添加进来时&#xff0c; 就需要挑选并舍弃原有的部分内容&#xff0c;从而腾出…

LRUCache 详解

LRU算法详解 一、什么是 LRU 算法 就是一种缓存淘汰策略。 计算机的缓存容量有限&#xff0c;如果缓存满了就要删除一些内容&#xff0c;给新内容腾位置。但问题是&#xff0c;删除哪些内容呢&#xff1f;我们肯定希望删掉哪些没什么用的缓存&#xff0c;而把有用的数据继续…

LRU Cache

前言 哈喽&#xff0c;各位小伙伴大家好&#xff0c;本章内容为大家介绍计算机当中为了提高数据相互传输时的效率而引进的一种重要设计结构叫做LRU Cache,下面将为大家详细介绍什么是LRU Cache,以及它是如何是实现的&#xff0c;如何提升效率的。 1.什么是LRU Cache? LRU是L…

uniapp日历原生插件

<template><!-- 打卡日历页面 --><view classall><view class"bar"><!-- 上一个月 --><view class"previous" click"handleCalendar(0)"><button class"barbtn">上一月</button><…

微信小程序使用日历插件

一&#xff0c;添加插件 1&#xff0c;在你的小程序关联的微信公众平台打开 设置》第三方服务》添加插件 2&#xff0c;直接AppID&#xff08;wx92c68dae5a8bb046&#xff09;搜索到该插件并申请授权&#xff0c;授权成功即可在小程序使用 二&#xff0c;小程序使用插件 app…

日历组件

日历组件&#xff1a; <template><div class"calendar" click.stop><div class"input-wrap"><inputtype"text"v-if"dateChangeSets":placeholder"placeholder"class"input dateChangeSets middle…

vue-calendar基于vue的日历插件

本文转载于https://www.cnblogs.com/zwhgithub/p/8005414.html vue-calendar-component 基于 vue 2.0 开发的轻量&#xff0c;高性能日历组件占用内存小&#xff0c;性能好&#xff0c;样式好看&#xff0c;可扩展性强原生 js 开发&#xff0c;没引入第三方库 效果 Install …

实用插件(一)日历插件——My97DatePicker

注&#xff1a;My97DatePicker插件仅限pc端使用&#xff0c;若是app项目&#xff0c;建议使用ICalendar或者Mobiscroll。 &#xff08;ICalendar插件在华为手机上存在兼容性问题&#xff0c;日期不能滚动&#xff0c;但使用很简单&#xff1b;Mobiscroll使用起来较为复杂&…

sys-calendar.js带节假日的日历插件

下载地址 sys-calendar.js带节假日的日历插件&#xff0c;代码引用比较多。 dd:

jquery日历插件,可自定义日期内容

效果图&#xff1a; 使用&#xff1a; <link href"static/css/raoCalendar.css" rel"stylesheet" type"text/css"><script src"static/js/jquery.min.js"></script> <script src"static/js/raoCalendar.js…

两款超好用js日历插件(fullcalendar和zabuto_calendar)

这两款插件特别类似,其实用其中一款即可。 先展示一下我用这两款插件制作的排班系统 这个是fullcalendar插件制作的排班页面,左边新建一系列组和组员,可以将人员直接拖拽至右边的日历上,不同组以颜色区别。 这个是将上面的排班内容用zabuto_calendar插件显示出来,黄色区域…

BootStrap日历插件

BootStrap日历插件 前端引入插件三大步骤 引入插件所需的资源文件 <%--引入BootStrap日历插件相关资源文件--%><%--按照资源文件相互依赖的顺序来引入--%><script type"text/javascript" src"jquery/jquery-1.11.1-min.js"></scrip…

jQuery实现移动端手机选择日期日历插件

效果图 calendar.css html, body {color: #333;margin: 0;height: 100%;font-family: "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;font-weig…

uniapp日历插件

日历插件 效果图一、使用方法二、组件编写&#xff0c;两个文件、直接上代码month.vuecalendar.vue 效果图 一、使用方法 <template><view><view class"" click"open"><text>展示日历{{value[0]}}-{{value[1]}}</text><…

calendar.js多种形式日历插件

下载地址 calendar.js是一款强大的日历插件&#xff0c;有多种形式的日历插件&#xff0c;比如&#xff0c;选择年、选择月、范围等。 dd:

日历插件:bootstrap-datetimepicker

1. 简述 前端插件使用步骤&#xff1a; 1)引入开发包&#xff1a;.js,.css 下载开发包&#xff0c;拷贝到项目webapp目录下 把开发包引入到jsp文件中&#xff1a;<link><script> 2)创建容器&#xff1a;<input type"text"…

最棒的 10 款 jQuery 日历插件

RT&#xff0c; 最棒的 10 款 jQuery 日历插件 做个记号&#xff0c;以后就不用再翻来翻去 1、JavaScript Calendar, JSCal2 地址&#xff1a;点击打开链接 2、Date Picker 地址&#xff1a;点击打开链接 3、 jQuery Frontier Calendar 地址&#xff1a;点击打开链接…