Android图片缓存
来源:互联网 发布:类似keynote的软件 编辑:程序博客网 时间:2024/06/03 19:09
在移动设备上,用户访问网络需要消耗宝贵的流量,因此缓存策略就变得尤为重要。例如当用户第一次从网络加载图片后,就将其缓存到存储设备上,这样当下一次使用到这张图片时就不再从网络中获取。很多时候往往还在内存中缓存一份。这样,当用户加载图片时,首先会从内存中获取,如果内存中没有,那么就从存储设备中获取,最后才从网络下下载这张图片。
目前常用的一种算法是LRU(Least Recently Used),近期最少使用算法,采用LRU算法的缓存有两种:LruCache和DiskLruCache,分别用来实现内存缓存 和存储设备缓存。那么就先来使用第一种,从内存中获取图片。
LruCache
LruCache是一个泛型类,它内部采用一个LinkedHashMap以强引用的方式存储外界缓存对象,其提供get和put的方法来完成缓存的获取和添加操作。
下面的ImageCache类完成了LruCache的创建过程,和实现了缓存的取get方法和缓存的存put方法。
/** * 图片缓存类 * Created by lhc on 2017/7/31. */public class ImageCache { //图片缓存 LruCache<String, Bitmap> mImageCache; public ImageCache(){ initImageCache(); } private void initImageCache() { int maxMemory= (int) (Runtime.getRuntime().maxMemory()/1024); int cacheSize = maxMemory / 4; mImageCache = new LruCache<String,Bitmap>(cacheSize){ @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getRowBytes()*bitmap.getHeight()/1024; } }; } public void put(String url, Bitmap bitmap){ mImageCache.put(url,bitmap); } public Bitmap get(String url){ return mImageCache.get(url); }}
接着便是图片加载的类,这里使用到同步的方式加载图片,在加载图片的时候,先判断内存中是否有,如果有则从内存中获取,如果没有再从网络中获取,这里使用到线程池开启新的线程下载图片,然后再在主线程中得到设置图片,更新UI。
** * 图片加载类 * Created by lhc on 2017/7/27. */public class ImageLoader { Handler mUiHandle = new Handler(Looper.getMainLooper()); ImageCache mImageCache; ExecutorService executorService; public ImageLoader(){ mImageCache= new ImageCache(); //只有核心线程并且这些线程不会被回收,能更加快速的响应外界的请求 executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); } /** * 显示图片 * @param url * @param imageView */ public void displayImage(final String url, final ImageView imageView) { //从内存中获取 Bitmap bitmap = mImageCache.get(url); if (bitmap != null) { Log.e("ImageLoader", "从内存中获取"); imageView.setImageBitmap(bitmap); return; } imageView.setTag(url); executorService.submit(new Runnable() { @Override public void run() { Bitmap bitmap = downloadImage(url); if (bitmap == null) { return; } //更新url if (imageView.getTag().equals(url)) { Log.e("ImageLoader", "从网络获取"); updateImageView(imageView, bitmap); } mImageCache.put(url, bitmap); } }); } /** * 显示图片 在主线程 * * @param imageView * @param bitmap */ private void updateImageView(final ImageView imageView, final Bitmap bitmap) { mUiHandle.post(new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } }); } /** * 下载图片 从网络 * * @param imageurl * @return */ private Bitmap downloadImage(String imageurl) { Bitmap bitmap = null; HttpURLConnection conn = null; InputStream is = null; try { URL url = new URL(imageurl); conn = (HttpURLConnection) url.openConnection(); is = conn.getInputStream(); bitmap = BitmapFactory.decodeStream(is); } catch (Exception e) { e.printStackTrace(); } finally { conn.disconnect(); try { if (is != null) is.close(); } catch (IOException e) { e.printStackTrace(); } } return bitmap; }}
最后是我们的测试类,在onCreate方法中new一个ImageLoader,点击Button获取图片,可以发现,第一次从网络下获取,第二次从内存中获取。
public class MainActivity extends AppCompatActivity { ImageLoader loader; private final String url = "http://www.zhlzw.com/UploadFiles/Article_UploadFiles/201204/20120412123929231.jpg"; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = (ImageView) findViewById(R.id.imageVIew); loader = new ImageLoader(); } public void button(View view) { loader.displayImage(url,imageView); }}
DiskLruCache
DiskLruCache用于实现存储设备缓存,Disk不属于Android SDK一部分,它的源码可以可以从这里获得。
下面的类完成了DiskLruCache的创建以及从缓存中获取,存入缓存的方法。Disk的创建通过DiskLruCache.open(cachefile,appVersion,valueCount,maxSize)静态方法获得。要存入缓存要通过Editor完成,要获得缓存要通过diskLruCache得到Snapshot对象,再得到输入流,从而获得图片。
注意这里存入缓存要通过访问网络得到输入流,然后存入文件的输出流中,要在子线程中进行,而设置图片更改UI要在主线程中,因此使用到了AsyncTask.
注意添加访问网络以及读写权限。
public class ImageDiskCache { DiskLruCache diskLruCache; public ImageDiskCache(Context context) { try { File file = getDiskCacheDir(context, "bitmap"); if (!file.exists()) { file.mkdirs(); } diskLruCache = DiskLruCache.open(file, 1, 1, 10 * 1024 * 1024); } catch (IOException e) { e.printStackTrace(); } } /** * 根据url存入缓存 * * @param urlstring */ private void put(final String urlstring) { try { String key = hashKeyForDisk(urlstring); DiskLruCache.Editor editor = diskLruCache.edit(key); if (editor != null) { OutputStream os = editor.newOutputStream(0); if (downloadUrlToStream(urlstring, os)) { editor.commit(); } else { editor.abort(); } } //这里记得刷新一下,同步到journal文件 diskLruCache.flush(); } catch (IOException e) { e.printStackTrace(); } } /** * 设置图片 异步线程 * * @param urlstring * @return */ public void get(final String urlstring, final ImageView imageView) { imageView.setTag(urlstring); new AsyncTask<String, Void, Bitmap>() { @Override protected Bitmap doInBackground(String... params) { Bitmap bitmap = null; try { String key = hashKeyForDisk(urlstring); DiskLruCache.Snapshot snapshot = diskLruCache.get(key); if (snapshot == null) { //如果没有找到对应的缓存,则准备从网络上请求数据,并写入缓存 put(urlstring); snapshot = diskLruCache.get(key); } InputStream is = snapshot.getInputStream(0); bitmap = BitmapFactory.decodeStream(is); } catch (IOException e) { e.printStackTrace(); } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { if (imageView.getTag().equals(urlstring)) { imageView.setImageBitmap(bitmap); } } }.execute(); } /** * 删除指定url的缓存 * * @param urlstring */ public void remove(String urlstring) { try { String key = hashKeyForDisk(urlstring); diskLruCache.remove(key); } catch (IOException e) { e.printStackTrace(); } } /** * 删除全部缓存 */ public void delete() { try { diskLruCache.delete(); } catch (IOException e) { e.printStackTrace(); } } /** * 必须运行在子线程 * 当下载网络图片时候,图片就可以通过这个文件输入流写到文件系统 * * @param urlstring * @param os * @return */ private boolean downloadUrlToStream(final String urlstring, final OutputStream os) { HttpURLConnection conn = null; BufferedInputStream bis = null; BufferedOutputStream bos = null; try { URL url = new URL(urlstring); conn = (HttpURLConnection) url.openConnection(); InputStream is = conn.getInputStream(); bis = new BufferedInputStream(is, 8 * 1024); bos = new BufferedOutputStream(os, 8 * 1024); int b; while ((b = bis.read()) != -1) { bos.write(b); } return true; } catch (Exception e) { e.printStackTrace(); } finally { conn.disconnect(); try { if (bis != null) bis.close(); if (bos != null) bos.close(); } catch (IOException e) { e.printStackTrace(); } } return false; } /** * 缓存存入的位置 * * @param context * @param filename * @return */ private File getDiskCacheDir(Context context, String filename) { String cacheFile; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) { cacheFile = context.getExternalCacheDir().getPath(); } else { cacheFile = context.getCacheDir().getPath(); } return new File(cacheFile); } /** * 把url转换成key,因为图片的url很可能存在特殊字符 * 一般使用url的md5值作为key * * @param url * @return */ private String hashKeyForDisk(String url) { String cacheKey = null; try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); messageDigest.update(url.getBytes()); cacheKey = byteToHexString(messageDigest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey = String.valueOf(url.hashCode()); } return cacheKey; } /** * 将字节转成十进制 * * @param digest * @return */ private String byteToHexString(byte[] digest) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < digest.length; i++) { String hex = Integer.toHexString(0xff & digest[i]); if (hex.length() == 1) { builder.append('0'); } builder.append(hex); } return builder.toString(); }}
然后 在主activity通过 diskCache.get(url,imageView);就能实现硬盘缓存了。
总结
在以上,分别实现了内存缓存和硬盘缓存,在实际开发过程中,往往使用两者相结合的方式,能更高效的加载图片,节省用户流量。使用例子见下一篇,Android照片墙,高效加载图片,敬请期待。
- Android缓存:图片缓存管理
- Android图片缓存,三级缓存
- android图片缓存
- Android 远程图片缓存
- Android 图片缓存
- Android图片缓存
- android多级图片缓存
- Android图片本地缓存
- android 缓存 图片
- android 图片缓存管理
- Android--SoftReference缓存图片
- Android图片缓存管理
- Android:图片缓存
- Android LruCache 缓存图片
- Android图片缓存
- Android 图片缓存处理
- android图片缓存
- android图片缓存
- ExcelVBA函数生成SQL.xlsm
- java memory model
- IDEA的常用操作(快捷键)
- initWithContentsOfFile遇到的大坑
- VMware 中hadoop 集群搭建步骤
- Android图片缓存
- C语言字符串查找知识点搬运
- docker swarm实践
- Eclipse 生成*.jar包
- HEVC picture type 帧类型详解 [待整理]
- 设计模式之模板方法模式
- 牛客网编程基础16,18(洪水)
- 浅析人脸检测之Haar分类器方法
- 参数为HashMap形式的增删改查命名SQL