Android实现图片异步请求加三级缓存
使用xutils等框架是很方便,但今天要用代码实现bitmaputils 的功能,很简单,
asynctask请求一张图片
####asynctask
#####asynctask是线程池+handler的封装 第一个泛型: 传参的参数类型类型(和doinbackground一致) 第二个泛型:
#####更新进度的参数类型(和onprogressupdate一致) 第三个泛型: 返回结果的参数类型(和onpostexecute一致,
#####和doinbackground返回类型一致)
看asynctask源码:
public abstract class asynctask<params, progress, result> { private static final string log_tag = "asynctask"; private static final int core_pool_size = 5; private static final int maximum_pool_size = 128; private static final int keep_alive = 1; private static final threadfactory sthreadfactory = new threadfactory() { private final atomicinteger mcount = new atomicinteger(1); public thread newthread(runnable r) { return new thread(r, "asynctask #" + mcount.getandincrement()); } };
核心线程5 最大线程128 这是asynctask的线程池 然后通过handler发送消息 , 它内部实例化了一个静态的自定义类 internalhandler,这个类是继承自 handler 的,在这个自定义类中绑定了一个叫做 asynctaskresult 的对象,每次子线程需要通知主线程,就调用 sendtotarget 发送消息给 handler自己。然后在 handler 的 handlemessage 中 asynctaskresult 根据消息的类型不同(例如 message_post_progress 会更新进度条,message_post_cancel 取消任务)而做不同的操作,值得一提的是,这些操作都是在ui线程进行的,意味着,从子线程一旦需要和 ui 线程交互,内部自动调用了 handler 对象把消息放在了主线程了。
private static final internalhandler shandler = new internalhandler(); mfuture = new futuretask<result>(mworker) { @override protected void more ...done() { message message; result result = null; try { result = get(); } catch (interruptedexception e) { android.util.log.w(log_tag, e); } catch (executionexception e) { throw new runtimeexception("an error occured while executing doinbackground()", e.getcause()); } catch (cancellationexception e) { message = shandler.obtainmessage(message_post_cancel, new asynctaskresult<result>(asynctask.this, (result[]) null)); message.sendtotarget(); return; } catch (throwable t) { throw new runtimeexception("an error occured while executing " + "doinbackground()", t); } message = shandler.obtainmessage(message_post_result, new asynctaskresult<result>(asynctask.this, result)); message.sendtotarget(); } }; private static class internalhandler extends handler { @suppresswarnings({"unchecked", "rawuseofparameterizedtype"}) @override public void more ...handlemessage(message msg) { asynctaskresult result = (asynctaskresult) msg.obj; switch (msg.what) { case message_post_result: // there is only one result result.mtask.finish(result.mdata[0]); break; case message_post_progress: result.mtask.onprogressupdate(result.mdata); break; case message_post_cancel: result.mtask.oncancelled(); break; } } }
下面看代码 第一步我们先请求一张图片 并解析 注释写的很详细了.
netcacheutils.java
import java.io.inputstream; import java.net.httpurlconnection; import java.net.url; import android.graphics.bitmap; import android.graphics.bitmapfactory; import android.os.asynctask; import android.widget.imageview; /** * 网络缓存 * * @author ace * @date 2016-02-18 */ public class netcacheutils { private localcacheutils mlocalutils; private memorycacheutils mmemoryutils; public netcacheutils(localcacheutils localutils, memorycacheutils memoryutils) { mlocalutils = localutils; mmemoryutils = memoryutils; } public void getbitmapfromnet(imageview imageview, string url) { bitmaptask task = new bitmaptask(); task.execute(imageview, url); } /** * asynctask是线程池+handler的封装 第一个泛型: 传参的参数类型类型(和doinbackground一致) 第二个泛型: * 更新进度的参数类型(和onprogressupdate一致) 第三个泛型: 返回结果的参数类型(和onpostexecute一致, * 和doinbackground返回类型一致) */ class bitmaptask extends asynctask<object, integer, bitmap> { private imageview mimageview; private string url; // 主线程运行, 预加载 @override protected void onpreexecute() { super.onpreexecute(); } // 子线程运行, 异步加载逻辑在此方法中处理 @override protected bitmap doinbackground(object... params) { mimageview = (imageview) params[0]; url = (string) params[1]; mimageview.settag(url);// 将imageview和url绑定在一起 // publishprogress(values)//通知进度 // 下载图片 return download(url); } // 主线程运行, 更新进度 @override protected void onprogressupdate(integer... values) { super.onprogressupdate(values); } // 主线程运行, 更新主界面 @override protected void onpostexecute(bitmap result) { if (result != null) { // 判断当前图片是否就是imageview要的图片, 防止listview重用导致的图片错乱的情况出现 string bindurl = (string) mimageview.gettag(); if (bindurl.equals(url)) { // 给imageview设置图片 mimageview.setimagebitmap(result); // 将图片保存在本地 mlocalutils.setbitmaptolocal(result, url); // 将图片保存在内存 mmemoryutils.setbitmaptomemory(url, result); } } } } /** * 下载图片 * * @param url */ public bitmap download(string url) { httpurlconnection conn = null; try { conn = (httpurlconnection) (new url(url).openconnection()); conn.setconnecttimeout(5000); conn.setreadtimeout(5000); conn.setrequestmethod("get"); conn.connect(); int responsecode = conn.getresponsecode(); if (responsecode == 200) { inputstream in = conn.getinputstream(); // 将流转化为bitmap对象 bitmap bitmap = bitmapfactory.decodestream(in); return bitmap; } } catch (exception e) { e.printstacktrace(); } finally { if (conn != null) { conn.disconnect(); } } return null; } }
memorycacheutils.java 用到了lrucache 很简单
我简单翻译下文档:
* a cache that holds strong references to a limited number of values. each time * a value is accessed, it is moved to the head of a queue. when a value is * added to a full cache, the value at the end of that queue is evicted and may * become eligible for garbage collection. * cache保存一个强引用来限制内容数量,每当item被访问的时候,此item就会移动到队列的头部。 * 当cache已满的时候加入新的item时,在队列尾部的item会被回收。 * <p>if your cached values hold resources that need to be explicitly released, * override {@link #entryremoved}. * 如果你cache的某个值需要明确释放,重写entryremoved() * <p>by default, the cache size is measured in the number of entries. override * {@link #sizeof} to size the cache in different units. for example, this cache * is limited to 4mib of bitmaps: 默认cache大小是测量的item的数量,重写sizeof计算不同item的 * 大小。 {@code * int cachesize = 4 * 1024 * 1024; // 4mib * lrucache<string, bitmap> bitmapcache = new lrucache<string, bitmap>(cachesize) { * protected int sizeof(string key, bitmap value) { * return value.getbytecount(); * } * }} ------------------------------------------------------------------- <p>this class is thread-safe. perform multiple cache operations atomically by * synchronizing on the cache: <pre> {@code * synchronized (cache) { * if (cache.get(key) == null) { * cache.put(key, value); * } * }}</pre> * 他是线程安全的,自动地执行多个缓存操作并且加锁 ------------------------- <p>this class does not allow null to be used as a key or value. a return * value of null from {@link #get}, {@link #put} or {@link #remove} is * unambiguous: the key was not in the cache. * 不允许key或者value为null * 当get(),put(),remove()返回值为null时,key相应的项不在cache中
最重要的大概就是以上几点: 使用很简单
来看代码:
import android.graphics.bitmap; import android.support.v4.util.lrucache; /** * 内存缓存工具类 * * @author ace * @date 2016-02-19 */ public class memorycacheutils { // android 2.3 (api level // 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠,建议用lrucache,它是强引用 private lrucache<string, bitmap> mcache; public memorycacheutils() { int maxmemory = (int) runtime.getruntime().maxmemory();// 获取虚拟机分配的最大内存 // 16m // lru 最近最少使用, 通过控制内存不要超过最大值(由开发者指定), 来解决内存溢出,就像上面翻译的所说 如果cache满了会清理最近最少使用的缓存对象 mcache = new lrucache<string, bitmap>(maxmemory / 8) { @override protected int sizeof(string key, bitmap value) { // 计算一个bitmap的大小 int size = value.getrowbytes() * value.getheight();// 每一行的字节数乘以高度 return size; } }; } public bitmap getbitmapfrommemory(string url) { return mcache.get(url); } public void setbitmaptomemory(string url, bitmap bitmap) { mcache.put(url, bitmap); } }
最后一级缓存 本地缓存 把网络下载的图片 文件名以md5的形式保存到内存卡的制定目录
/** * 本地缓存工具类 * * @author ace * @date 2016-02-19 */ public class localcacheutils { // 图片缓存的文件夹 public static final string dir_path = environment .getexternalstoragedirectory().getabsolutepath() + "/ace_bitmap_cache"; public bitmap getbitmapfromlocal(string url) { try { file file = new file(dir_path, md5encoder.encode(url)); if (file.exists()) { bitmap bitmap = bitmapfactory.decodestream(new fileinputstream( file)); return bitmap; } } catch (exception e) { e.printstacktrace(); } return null; } public void setbitmaptolocal(bitmap bitmap, string url) { file dirfile = new file(dir_path); // 创建文件夹 文件夹不存在或者它不是文件夹 则创建一个文件夹.mkdirs,mkdir的区别在于假如文件夹有好几层路径的话,前者会创建缺失的父目录 后者不会创建这些父目录 if (!dirfile.exists() || !dirfile.isdirectory()) { dirfile.mkdirs(); } try { file file = new file(dir_path, md5encoder.encode(url)); // 将图片压缩保存在本地,参1:压缩格式;参2:压缩质量(0-100);参3:输出流 bitmap.compress(compressformat.jpeg, 100, new fileoutputstream(file)); } catch (exception e) { e.printstacktrace(); } } }
md5encoder
import java.security.messagedigest; public class md5encoder { public static string encode(string string) throws exception { byte[] hash = messagedigest.getinstance("md5").digest(string.getbytes("utf-8")); stringbuilder hex = new stringbuilder(hash.length * 2); for (byte b : hash) { if ((b & 0xff) < 0x10) { hex.append("0"); } hex.append(integer.tohexstring(b & 0xff)); } return hex.tostring(); } }
最后新建一个工具类来使用我们上面的三个缓存方法
/** * 三级缓存工具类 * * @author ace * @date 2016-02-19 */ public class mybitmaputils { // 网络缓存工具类 private netcacheutils mnetutils; // 本地缓存工具类 private localcacheutils mlocalutils; // 内存缓存工具类 private memorycacheutils mmemoryutils; public mybitmaputils() { mmemoryutils = new memorycacheutils(); mlocalutils = new localcacheutils(); mnetutils = new netcacheutils(mlocalutils, mmemoryutils); } public void display(imageview imageview, string url) { // 设置默认加载图片 imageview.setimageresource(r.drawable.news_pic_default); // 先从内存缓存加载 bitmap bitmap = mmemoryutils.getbitmapfrommemory(url); if (bitmap != null) { imageview.setimagebitmap(bitmap); system.out.println("从内存读取图片啦..."); return; } // 再从本地缓存加载 bitmap = mlocalutils.getbitmapfromlocal(url); if (bitmap != null) { imageview.setimagebitmap(bitmap); system.out.println("从本地读取图片啦..."); // 给内存设置图片 mmemoryutils.setbitmaptomemory(url, bitmap); return; } // 从网络缓存加载 mnetutils.getbitmapfromnet(imageview, url); } }
以上就是本文的全部内容,希望对大家学习android软件编程有所帮助。
上一篇: JAXB简介_动力节点Java学院整理