Android 图片如何高效加载与缓存 —— (3) 改进和增加功能
2016-03-06 01:59
597 查看
再上一个文章中还有很多问题:比如要取消某个图片拉取任务、要在图片加载过程中的图片效果修改、一些逻辑上的问题,这次来一一修改!!
( 之前忽略了这个 FutureTask ,还以为是ION里面的东西,没想到这东西是自带的QAQ 唉。。 )
我们用其中一个作为例子,这个是网络拉取任务的,其他的任务创建也是如此。
第二步:自定义线程池,实现任务的控制
最后,制作供外部调用的接口:
我们还是用网络拉取的任务做例子:
图片处理的调用:
一,使任务可取消移除
第一步我们使用FutureTask和 Callable 进行搭配,来创建一项任务。( 之前忽略了这个 FutureTask ,还以为是ION里面的东西,没想到这东西是自带的QAQ 唉。。 )
我们用其中一个作为例子,这个是网络拉取任务的,其他的任务创建也是如此。
private class Fetcher implements Callable<String>,ThreadinterFace { //网络拉取任务 ... ... @Override public String call() throws Exception { if (imageView != null || onImageLoad != null){ OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(5,TimeUnit.SECONDS).build(); Request request = new Request.Builder().url(imageUrl).build(); try { Call call = okHttpClient.newCall(request); Response response = call.execute(); cacheImage = BitmapFactory.decodeStream(response.body().byteStream()); if (cacheImage != null) imageCacher.putCache(tag,cacheImage); if (onLoaded != null) cacheImage = onLoaded.reduce(cacheImage,tag); } catch (IOException e) { Log.e("OCImageLoader", "\nurl:" + imageUrl +"\nError:"+ e.toString()); onError(0); } runOnUIThread(new Runnable() { @Override public void run() { Log.d("OCImageLoader","Image :"+imageUrl+" downloaded"); onDownloadCompleted(cacheImage,imageView,tag,onImageLoad); } }); }else { runOnUIThread(new Runnable() { @Override public void run() { onError(1); } }); } return null; } ... ... }
第二步:自定义线程池,实现任务的控制
public class OCThreadExecutor extends ThreadPoolExecutor { //这里我们使用HashMap来保存每个任务的引用,以用来处理 private Map<String,FutureTask> runnableMap; public OCThreadExecutor(int maxRunningThread, String poolName) { super(maxRunningThread, maxRunningThread, 0l, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new OCThreadFactory(poolName)); runnableMap = new HashMap<>(); } public void submit(FutureTask task , String tag){ //执行线程 if ( !runnableMap.containsKey(tag) ){ //如果池内没有相同的任务则可以执行 runnableMap.put(tag, task); submit(task); }else { Log.d("OCThreadExecutor", "Same task TAG. Skipped. "); } } public boolean cancelTask(String tag){ //中断任务 //如果请求的tag对应的任务在列表中 if ( runnableMap.containsKey(tag) ){ //先将任务从队列中移除 remove(runnableMap.get(tag)); //无论任务的状态是什么,一律任务终止,然后移除引用 runnableMap.remove(tag).cancel(true); return true; }else{ Log.d("OCThreadExecutor", "TAG dose not exist. Skipped. "); return false; } } public boolean cancelAllTask(){ //终止所有任务 //用迭代器来遍历所有任务 Iterator<FutureTask> taskList = runnableMap.values().iterator(); int count = 0; while (taskList.hasNext()){ count++; FutureTask task = taskList.next(); task.cancel(true); remove(task); } runnableMap.clear(); Log.d("OCThreadExecutor",count +" Tasks canceled."); return count > 0; } public boolean removeTag(String tag){ //移除TAG,调用于任务结束时 if (runnableMap.remove(tag) != null){ Log.d("OCThreadExecutor","TAG removed."); return true; }else { Log.d("OCThreadExecutor","TAG dose not exist. Skipped. "); return false; } } static class OCThreadFactory implements ThreadFactory {...} static class OCThread extends Thread {...}
最后,制作供外部调用的接口:
//终止任务 public void cancelTask(String tag){ //优先终止网络拉取线程池内的任务 fetherExecutor.cancelTask(tag); //再终止缓存线程池内的任务 cacheExecutor.cancelTask(tag); } //终止所有任务 public void cancelAllTask(){ //优先终止网络拉取线程池内的所有任务 fetherExecutor.cancelAllTask(); //再终止缓存线程池内的所有任务 cacheExecutor.cancelAllTask(); }
第二,在图片加载中处理图片
我们要让图片的处理在图片加载的线程中同时处理,这样实现的方式也挺简单的,就是在图片加载完成之后,通过接口回调,传入原图,传出处理后的图片即可!我们还是用网络拉取的任务做例子:
private class Fetcher implements Callable<String>,ThreadinterFace { ... ... @Override public String call() throws Exception { if (imageView != null || onImageLoad != null){ OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(5,TimeUnit.SECONDS).build(); Request request = new Request.Builder().url(imageUrl).build(); try { Call call = okHttpClient.newCall(request); Response response = call.execute(); cacheImage = BitmapFactory.decodeStream(response.body().byteStream()); if (cacheImage != null) imageCacher.putCache(tag,cacheImage); //这里就把图片进行处理了,并传出 if (onLoaded != null) cacheImage = onLoaded.reduce(cacheImage,tag); } catch (IOException e) { Log.e("OCImageLoader", "\nurl:" + imageUrl +"\nError:"+ e.toString()); onError(0); } runOnUIThread(new Runnable() { @Override public void run() { Log.d("OCImageLoader","Image :"+imageUrl+" downloaded"); onDownloadCompleted(cacheImage,imageView,tag,onImageLoad); } }); }else { runOnUIThread(new Runnable() { @Override public void run() { onError(1); } }); } return null; }
图片处理的调用:
OCImageLoader.loader().loadImage(任务tag, 任务加载网址, imageView对象, new OnImageLoad() { @Override public void onLoadCompleted(Bitmap image, String tag) { //图片加载完成后的操作 ... ... } @Override public void onLoadFailed() { //图片加载失败后的操作 ... ... } }, new HandleOnLoaded() { @Override public Bitmap reduce(Bitmap bitmap, String tag) { //使用传入的bitmap原图进行处理,Return传出的是处理后的。在子线程中 return Blur.fastblur(UserDetailActivity.this,bitmap,20); } });
第三,增加图片异步处理任务
写完了才想起之前写过这个了。。。不管了,这次的解释比上次的更清楚 -_-private class ReduceImageEffect implements Callable<String>,ThreadinterFace{ private OnHandleBitmap onHandleBitmap; //处理回调接口 private boolean cacheAsFile; //是否要本地缓存 private BitmapFactory.Options options; private Bitmap bitmap; //事先提供处理对象 private String path , url; //本地路径或网址 private String cacheTAG; //图片的唯一标签 public ReduceImageEffect(OnHandleBitmap onHandleBitmap, Bitmap bitmap,BitmapFactory.Options options,boolean cacheAsFile,String path,String url,String id) { this.onHandleBitmap = onHandleBitmap; this.options = options; this.bitmap = bitmap; this.cacheAsFile = cacheAsFile; this.url = url; this.path = path; this.cacheTAG = id; } @Override public String call() throws Exception { //如果并没有提供 Bitmap 对象,或者需要带Option读取图片的 if(bitmap == null || options != null){ //我们先尝试从 LRU缓存 内读取之前处理过的图片 //这里要说下,因为TAG对应的是唯一的处理效果的图片,所以并不会提取到其他的资源 bitmap = imageCacher.getByLruCache(cacheTAG); if (bitmap == null){ //如果LRU内并没有,则尝试从本地缓存文件获取 Log.e("OCImageLoader","No reduced LRUcache.Trying to load reduced File cache..."); bitmap = imageCacher.getByFileCache(cacheTAG); if (bitmap == null){ //如果还是没有读取到数据,则尝试读取原始数据 Log.e("OCImageLoader","No reduced File cache.Trying to load original File cache..."); String cachePath = null; if (url != null){ //如果提供了网络地址,则尝试读取该网址对应的本地缓存文件的路径 cachePath = imageCacher.getCacheFile(buildTag(url)); }else if (path != null){ //如果提供了文件路径,则先尝试读取这个文件的本地缓存文件的路径 cachePath = imageCacher.getCacheFile(buildTag(path)); } if (cachePath == null && path != null && imageCacher.isCanCacheAsFile()){ //如果依旧没有任何本地缓存,同时指定了路径而且应用有本地读取权限 //使用Option(如果option为NULL也可)来读取本地文件 Log.e("OCImageLoader","No original File cache.Trying to load original File by path..."); bitmap = BitmapFactory.decodeFile(path,options); }else if (cachePath != null){ //如果有缓存的路径 //使用Option(如果option为NULL也可)来读取本地缓存 bitmap = BitmapFactory.decodeFile(cachePath,options); } } }else { Log.d("OCImageLoader","LRUcache found."); } }else{ Log.d("OCImageLoader","Option is NULL , using original cache"); } if (bitmap != null && onHandleBitmap != null){ //如果提供或者是得到了 Bitmap 对象,并且处理回调接口不为空 //通过回调接口处理对象 bitmap = onHandleBitmap.onAsynHandleBitmap(path, bitmap); if (bitmap != null){ //若处理后的Bitmap不为空 if (cacheAsFile){ //如果要求本地缓存,则进行缓存 Log.d("OCImageLoader","Tag:"+cacheTAG+" Cached as LRU & File "); imageCacher.putCache(cacheTAG,bitmap); }else { //不然只进行 LRU缓存 Log.d("OCImageLoader","Tag:"+cacheTAG+" Cached as LRU "); imageCacher.putInLruCaches(cacheTAG,bitmap); } //移除线程池里的标签 cacheExecutor.removeTag(cacheTAG); runOnUIThread(new Runnable() { @Override public void run() { //返回UI线程,传回处理完成的对象 onHandleBitmap.onReduceCompleted(bitmap); } }); }else { //如果处理后的Bitmap为空,则使用读取失败回调接口 Log.e("OCImageLoader","Bitmap become NULL , after onHandleBitmap."); runOnUIThread(new Runnable() { @Override public void run() { onError(2); } }); } }else { //如果怎么都读取不到Bitmap对象,则使用读取失败回调接口 Log.e("OCImageLoader","Failed to load bitmap..."); runOnUIThread(new Runnable() { @Override public void run() { onError(0); } }); } return null; } @Override public void onDownloadCompleted(Bitmap bitmap, ImageView imageView, String tag, OnImageLoad onImageLoadCompleted) { } @Override public void onError(int status) { cacheExecutor.removeTag(cacheTAG); } @Override public int hashCode() { return cacheTAG.hashCode(); } @Override public boolean equals(Object o) { return this.hashCode() == o.hashCode() && o instanceof ReduceImageEffect; } }
第四,之前发现的逻辑错误修正
懵比了 QwQ..之前在图片读取完成之后的逻辑有问题,会导致回调出问题@Override public void onDownloadCompleted(Bitmap bitmap, ImageView imageView, String tag, OnImageLoad onImageLoadCompleted) { //当图片读取完成 //移除线程池内的标签 cacheExecutor.removeTag(tag); if (bitmap != null && imageView != null && imageView.getTag().equals(tag)){ //如果得到的Bitmap对象不为空,目标ImageView不为空,同时目标ImageView为当初指定的那个 if (durationMillis > 0 ){ //若有淡出淡入的需求则展示出来 Drawable prevDrawable = imageView.getDrawable(); if (prevDrawable == null) { prevDrawable = new ColorDrawable(TRANSPARENT); } Drawable nextDrawable = new BitmapDrawable(imageView.getResources(), bitmap); TransitionDrawable transitionDrawable = new TransitionDrawable( new Drawable[] { prevDrawable, nextDrawable }); imageView.setImageDrawable(transitionDrawable); transitionDrawable.startTransition(durationMillis); }else { imageView.setImageBitmap(bitmap); } } if (onImageLoadCompleted != null){ //如果回调接口不为空,则使用 onImageLoadCompleted.onLoadCompleted(bitmap,tag); } }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件