Android中图片的三级缓存机制
我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以android中引入了图片的缓存这一操作机制。
原理:
首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存缓存中,缓存到强引用中 也就是lrucache中。如果强引用中空间不足,就会将较早存储的图片对象驱逐到软引用(softreference)中存储,然后将图片缓存到文件(内部存储外部存储)中;读取图片的时候,先读取内存缓存,判断强引用中是否存在图片,如果强引用中存在,则直接读取,如果强引用中不存在,则判断软引用中是否存在,如果软引用中存在,则将软引用中的图片添加到强引用中并且删除软引用中的数据,如果软引用中不存在,则读取文件存储,如果文件存储不存在,则网络加载。
下载: 网络--内存--文件
读取: 内存--强引用--软引用--文件--网络
也就是这样的一个过程,下面用一个简单地demo来演示一下图片你的三级缓存,此demo中只有一个界面,界面上一个imageview用来显示图片,一个按钮用来点击的时候加载图片。布局如下:
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <imageview android:id="@+id/iv_img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher" android:layout_centerinparent="true"/> <button android:id="@+id/btn_download" android:layout_below="@+id/iv_img" android:layout_centerhorizontal="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="加载图片"/> </relativelayout>
因为要从网络下载数据,还要存储到本地sd卡中,所以不要忘了为程序添加网络访问的权限、网络状态访问的权限和向外部存储设备写内容的权限:
<uses-permission android:name="android.permission.internet" /> <uses-permission android:name="android.permission.access_network_state" /> <uses-permission android:name="android.permission.write_external_storage" />
接着,创建一个 httputils 工具类用于访问网络,代码如下:
package com.yztc.lx.cashimg; import android.content.context; import android.net.connectivitymanager; import android.net.networkinfo; import java.io.bytearrayoutputstream; import java.io.ioexception; import java.io.inputstream; import java.net.httpurlconnection; import java.net.url; /**网络访问工具类 * created by lx on 2016/8/19. */ public class httputils { /** * 判断网络连接是否通畅 * @param mcontext * @return */ public static boolean isnetconn(context mcontext) { connectivitymanager manager = (connectivitymanager) mcontext.getsystemservice(context.connectivity_service); networkinfo info = manager.getactivenetworkinfo(); if (info != null) { return info.isconnected(); } else { return false; } } /** * 根据path下载网络上的数据 * @param path 路径 * @return 返回下载内容的byte数据形式 */ public static byte[] getdatefromnet(string path) { bytearrayoutputstream baos = new bytearrayoutputstream(); try { url url = new url(path); httpurlconnection conn = (httpurlconnection) url.openconnection(); conn.setrequestmethod("get"); conn.setconnecttimeout(5000); conn.setdoinput(true); conn.connect(); if (conn.getresponsecode()==200) { inputstream is = conn.getinputstream(); byte b[] = new byte[1024]; int len; while ((len=is.read(b))!=-1) { baos.write(b, 0, len); } return baos.tobytearray(); } } catch (ioexception e) { e.printstacktrace(); } return baos.tobytearray(); } }
还有操作外部存储的工具类:
package com.yztc.lx.cashimg; import android.graphics.bitmap; import android.graphics.bitmapfactory; import android.os.environment; import java.io.bytearrayoutputstream; import java.io.file; import java.io.fileinputstream; import java.io.fileoutputstream; import java.io.ioexception; /** * created by lx on 2016/8/20. */ public class externalstorageutils { /** * 将传递过来的图片byte数组存储到sd卡中 * @param imgname 图片的名字 * @param buff byte数组 * @return 返回是否存储成功 */ public static boolean storetosdroot(string imgname, byte buff[]) { boolean b = false; string basepath = environment.getexternalstoragedirectory().getabsolutepath(); file file = new file(basepath, imgname); try { fileoutputstream fos = new fileoutputstream(file); fos.write(buff); fos.close(); b = true; } catch (ioexception e) { e.printstacktrace(); } return b; } /** * 从本地内存中根据图片名字获取图片 * @param imgname 图片名字 * @return 返回图片的bitmap格式 */ public static bitmap getimgfromsdroot(string imgname) { bitmap bitmap = null; string basepath = environment.getexternalstoragedirectory().getabsolutepath(); file file = new file(basepath, imgname); try { fileinputstream fis = new fileinputstream(file); bytearrayoutputstream baos = new bytearrayoutputstream(); byte b[] = new byte[1024]; int len; while ((len = fis.read(b)) != -1) { baos.write(b, 0, len); } byte buff[] = baos.tobytearray(); if (buff != null && buff.length != 0) { bitmap = bitmapfactory.decodebytearray(buff, 0, buff.length); } } catch (ioexception e) { e.printstacktrace(); } return bitmap; } }
本例中将图片默认存在了sd卡根目录中。
然后是最主要的主函数了:
package com.yztc.lx.cashimg; import android.graphics.bitmap; import android.graphics.bitmapfactory; import android.os.bundle; import android.os.handler; import android.os.message; import android.support.v7.app.appcompatactivity; import android.util.log; import android.util.lrucache; import android.view.view; import android.widget.button; import android.widget.imageview; import android.widget.toast; import java.lang.ref.softreference; import java.util.linkedhashmap; public class mainactivity extends appcompatactivity implements view.onclicklistener { private button btn_download; private imageview iv_img; private mylrucache mylrucache; private linkedhashmap<string, softreference<bitmap>> cashmap = new linkedhashmap<>(); private static final string tag = "mainactivity"; private string imgpath = "http://www.3dmgame.com/uploadfiles/201212/medium_20121217143424221.jpg"; private handler handler = new handler() { @override public void handlemessage(message msg) { bitmap bitmap = (bitmap) msg.obj; iv_img.setimagebitmap(bitmap); toast.maketext(mainactivity.this, "从网络上下载图片", toast.length_short).show(); } }; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initview(); int totalmemory = (int) runtime.getruntime().maxmemory(); int size = totalmemory / 8; mylrucache = new mylrucache(size); btn_download.setonclicklistener(this); } private void initview() { btn_download = (button) findviewbyid(r.id.btn_download); iv_img = (imageview) findviewbyid(r.id.iv_img); } @override public void onclick(view v) { bitmap b = getimgcache(); if (b != null) { iv_img.setimagebitmap(b); } else { new thread(new runnable() { @override public void run() { if (httputils.isnetconn(mainactivity.this)) { byte b[] = httputils.getdatefromnet(imgpath); if (b != null && b.length != 0) { bitmap bitmap = bitmapfactory.decodebytearray(b, 0, b.length); message msg = message.obtain(); msg.obj = bitmap; handler.sendmessage(msg); mylrucache.put(imgpath, bitmap); log.d(tag, "run: " + "缓存到强引用中成功"); boolean bl = externalstorageutils.storetosdroot("haha.jpg", b); if (bl) { log.d(tag, "run: " + "缓存到本地内存成功"); } else { log.d(tag, "run: " + "缓存到本地内存失败"); } } else { toast.maketext(mainactivity.this, "下载失败!", toast.length_short).show(); } } else { toast.maketext(mainactivity.this, "请检查你的网络!", toast.length_short).show(); } } }).start(); } } /** * 从缓存中获取图片 * * @return 返回获取到的bitmap */ public bitmap getimgcache() { bitmap bitmap = mylrucache.get(imgpath); if (bitmap != null) { log.d(tag, "getimgcache: " + "从lrucache获取图片"); } else { softreference<bitmap> sr = cashmap.get(imgpath); if (sr != null) { bitmap = sr.get(); mylrucache.put(imgpath, bitmap); cashmap.remove(imgpath); log.d(tag, "getimgcache: " + "从软引用获取图片"); } else { bitmap = externalstorageutils.getimgfromsdroot("haha.jpg"); log.d(tag, "getimgcache: " + "从外部存储获取图片"); } } return bitmap; } /** * 自定义一个方法继承系统的lrucache方法 */ public class mylrucache extends lrucache<string, bitmap> { /** * 必须重写的构造函数,定义强引用缓存区的大小 * @param maxsize for caches that do not override {@link #sizeof}, this is * the maximum number of entries in the cache. for all other caches, * this is the maximum sum of the sizes of the entries in this cache. */ public mylrucache(int maxsize) { super(maxsize); } //返回每个图片的大小 @override protected int sizeof(string key, bitmap value) { //获取当前变量每行的字节数和行高度(基本是固定写法,记不住给我背!) return value.getrowbytes() * value.getheight(); } /** * 当lrucache中的数据被驱逐或是移除时回调的函数 * * @param evicted 当lrucache中的数据被驱逐用来给新的value倒出空间的时候变化 * @param key 用来标示对象的键,一般put的时候传入图片的url地址 * @param oldvalue 之前存储的旧的对象 * @param newvalue 存储的新的对象 */ @override protected void entryremoved(boolean evicted, string key, bitmap oldvalue, bitmap newvalue) { if (evicted) { /** * 将旧的值存到软引用中,因为强引用中可能有多个值被驱逐, * 所以创建一个linkedhashmap<string, softreference<bitmap>>来存储软引用 * 基本也是固定写法 */ softreference<bitmap> softreference = new softreference<bitmap>(oldvalue); cashmap.put(key, softreference); } } } }
基本的思路都在代码注释中写的很详细了,主要就是要自定义一个类,来继承系统的lrucache,实现其中的两个主要的方法sizeof()和entryremoved(),还有就是必须重写它的构造函数。
以上所述是小编给大家介绍的android中图片的三级缓存机制的全部叙述,希望对大家有所帮助
上一篇: ASP.NET DropDownList控件的使用方法
下一篇: java贪吃蛇极速版
推荐阅读
-
Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
-
Android中图片的三级缓存机制
-
Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
-
Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析
-
Android中简单调用图片、视频、音频、录音和拍照的方法
-
Android基于SoftReference缓存图片的方法
-
android换肤功能 如何动态获取控件中背景图片的资源id?
-
Android实现从缓存中读取图片与异步加载功能类
-
android中图片加载到内存的实例代码
-
Android中TextView显示插入的图片实现方法