再上一个文章中还有很多问题:比如要取消某个图片拉取任务、要在图片加载过程中的图片效果修改、一些逻辑上的问题,这次来一一修改!!
第一步我们使用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);
}
}