欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android XImageLoader框架知识详解

程序员文章站 2022-05-27 08:16:58
1.前言 在做android也有一段时间了,在以往的习惯中或者说作为初学者的开发者来说,在项目周期很紧张的情况之下,迫于周期总是喜欢直接在网上找别人写的项目或者框架来用,但是一旦出问题总是看着别人的...

1.前言

在做android也有一段时间了,在以往的习惯中或者说作为初学者的开发者来说,在项目周期很紧张的情况之下,迫于周期总是喜欢直接在网上找别人写的项目或者框架来用,但是一旦出问题总是看着别人的代码是很头痛得事情,在此种种的情况之下,萌生自己写框架的想法,这样自己维护自己的框架那么就没那么痛苦了,想到目前app项目中用得最多的也是图片异步加载,那么就决定此次对图片加载进行开刀,说实在的对于图片加载框架目前已经有很多框架了比如imageloader、picasso、glide、fresco;这几种目前是用得最多的图片加载框架了;就个人习惯总觉得有点不是很习惯吧;当然只是个人习惯哈,目前支持gif图片加载和普通图片都支持的加载glide、fresco、并且还要支持图片的gif自定义形状的估计也就少了吧;目前自己写的框架支持gif图片和普通图片加载,并且可以显示圆角、圆形图片,支持自定图片形状,其中gif图片加载对android drawable gif开源框架的基础上修改部分;支持okhttp 3.0网络框架下载,通知支持自定网络工具自定义。

2.框架设计

Android XImageLoader框架知识详解

框架中需要储备知识点:

executorservice 线程池管理线程 semaphore 信号量操作,在图片加载实现队列式加载,你可以直接写队列,不一定要用这个。 lrucache 图片缓存,关于关于图片缓存你如果不用android的类,你可以用softreference(软应用)自己写,缓存的目的是避免图片的大量加载的时候会出现oom。

handler 异步处理图片显示问题

以上知识点了解清楚之后就可以开工写框架了,当然我这里只是我框架设计这么写,你自己有想法就按照自己的设计方案来写自己的框架。知识点解释完了,我就直接上代码了,我这个人比较直接。

3框架使用


普通图片和gif图片同时可以加载:ximageloader_with_gif 下载

只支持普通不支持gif图片加载 : ximageloader_without_gif 下载


jar配置方法:

jar文件下载后,androidstudio中复制到app/libs/, jnilibs文件夹直接复制到app/src/main/,values中文件复制到项目中,

对jar文件右键,选择”add as library…..”。

arr配置方法:

arr文件复制到app/libs/,在app/build.gradle 中dependencies里面配置: compile(name: ‘ximageloader’, ext: ‘aar’)

- xml view使用

(1)只是加载普通的图片,没有gif图片:

 
(2)加载图片有普通的图片和gif图片,并且不知道是不是gif图片还是普通图片
 

- 加载图片方法
(1)普通加载方式

/**
 * 显示图片
 *
 * @param target 图片view
 * @param resources 网络地址、本地地址、assets文件夹下面的文件名
*/
ximageloader.show(iv_target, resource);

(2)用默认图片的加载方式

/**
 * 显示图片
 *
 * @param target 图片view
 * @param resources 网络地址、本地地址、assets文件夹下面的文件名
 * @param defaultresid 默认图片资源id
*/
ximageloader.show(iv_target, resource,r.drawable.ic_default)

(3)圆角图片加载

/**
 * 显示图片
 *
 * @param target 图片view
 * @param resources 网络地址、本地地址、assets文件夹下面的文件名
 * @param defaultresid 默认图片资源id
 * @param transform 自定义图片显示形状,需要实现imagetransform接口
 */
ximageloader.show(holder.iv_target, resource, r.mipmap.ic_launcher, new imagecornerradiustransform(8));

(4)圆形图片加载

/**
 * 显示图片
 *
 * @param target 图片view
 * @param resources 网络地址、本地地址、assets文件夹下面的文件名
 * @param defaultresid 默认图片资源id
 * @param transform 自定义图片显示形状,需要实现imagetransform接口
 */
ximageloader.show(holder.iv_target, resource, r.mipmap.ic_launcher, new imagecircletransform(8, color.cyan));

-图片下载网络方式

ximageloader是以okhttp网络访问下载图片为基础的,如果你项目是httpurlconnect或者你不喜欢用okhttp的话就继承imagetask类,重写download()方法就行了。

import android.util.log;

import com.android.image.loader.imagehandler;
import com.android.image.loader.imageholder;
import com.android.image.loader.imagetask;

import java.io.file;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.io.inputstream;

import okhttp3.okhttpclient;
import okhttp3.request;
import okhttp3.response;


/**
 * created by relin
 * on 2018-06-25.
 * okhttp网络请求方式下载图片
 * 如果你希望利用urlhttpconnect下载图片
 * 请继承imagetask此类来写对应的文件下载
 * 具体的操作请看imagetask操作类的说明。
 */

public class okhttpimagetask extends imagetask {

 /**
  * 构造方法
  *
  * @param imageholder  图片数据对象
  * @param imagehandler
  */
 public okhttpimagetask(imageholder imageholder, imagehandler imagehandler) {
  super(imageholder, imagehandler);
 }

 @override
 protected void download(string url, file file) {
  request request = new request.builder().url(url).build();
  okhttpclient okhttpclient = new okhttpclient();
  response response = null;
  try {
response = okhttpclient.newcall(request).execute();
  } catch (ioexception e) {
e.printstacktrace();
  }
  if (response.issuccessful()) {
inputstream is = null;
byte[] buf = new byte[2048];
int len = 0;
fileoutputstream fos = null;
try {
 is = response.body().bytestream();
 long total = response.body().contentlength();
 fos = new fileoutputstream(file);
 long sum = 0;
 while ((len = is.read(buf)) != -1) {
  fos.write(buf, 0, len);
  sum += len;
  int progress = (int) (sum * 1.0f / total * 100);
 }
 fos.flush();
} catch (exception e) {
 log.e(this.getclass().getsimplename(), "exception " + e.tostring());
} finally {
 try {
  if (is != null)
is.close();
  if (fos != null) {
fos.close();
  }
 } catch (ioexception e) {
  log.e(this.getclass().getsimplename(), "ioexception " + e.tostring());
 }
}
  } else {
log.e(this.getclass().getsimplename(), "response is not successful");
  }
 }

}

4.框架实现

imageholder(图片数据类)

承载图片的主要数据的类,包含图片的加载对象、图片的形状、资源地址、线程信号量……

import android.content.context;
import android.graphics.bitmap;
import android.widget.imageview;

import com.android.image.transforms.imagetransform;

import java.io.file;
import java.util.concurrent.semaphore;

/**
 * created by relin
 * on 2018-06-27.
 */

public class imageholder {

 private context context;

 /**
  * 图片网络地址
  */
 private string url;
 /**
  * 图片显示view
  */
 private imageview target;
 /**
  * 图片缓存文件夹
  */
 private string cachefolder;
 /**
  * 图片默认资源文件id
  */
 private int defaultresid = 0;
 /**
  * 图片默认资源bitmap
  */
 private bitmap defaultbitmap;
 /**
  * 图片文件
  */
 private file file;
 /**
  * 线程信号量
  */
 private semaphore semaphore;
 /**
  * 图片处理标志
  */
 private int imagehandlerwhat;
 /**
  * 图片参数
  */
 private imageoptions imageoptions;
 /**
  * 图片形状[接口]
  */
 private imagetransform imagetransform;

 /**
  * 获取图片地址
  *
  * @return
  */
 public string geturl() {
  return url;
 }

 /**
  * 设置图片地址
  *
  * @param url
  */
 public void seturl(string url) {
  this.url = url;
 }

 /**
  * 显示图片的控件
  *
  * @return
  */
 public imageview gettarget() {
  return target;
 }

 /**
  * 设置图片控件
  *
  * @param target
  */
 public void settarget(imageview target) {
  this.target = target;
 }

 /**
  * 获取缓存文件夹[带路径]
  *
  * @return
  */
 public string getcachefolder() {
  return cachefolder;
 }

 /**
  * 设置缓存文件夹[带路径]
  *
  * @param cachefolder
  */
 public void setcachefolder(string cachefolder) {
  this.cachefolder = cachefolder;
 }

 /**
  * 获取图片文件
  *
  * @return
  */
 public file getfile() {
  return file;
 }

 /**
  * 设置图片文件
  *
  * @param file
  */
 public void setfile(file file) {
  this.file = file;
 }

 public semaphore getsemaphore() {
  return semaphore;
 }

 /**
  * 请求信号量
  */
 public void acquiresemaphore() {
  if (semaphore != null) {
try {
 semaphore.acquire();
} catch (interruptedexception e) {
 e.printstacktrace();
}
  }
 }

 /**
  * 释放信号量
  */
 public void releasesemaphore() {
  if (semaphore != null) {
semaphore.release();
  }
 }

 /**
  * 设置信号量
  *
  * @param semaphore
  */
 public void setsemaphore(semaphore semaphore) {
  this.semaphore = semaphore;
 }

 /**
  * 获取默认图片资源id
  *
  * @return
  */
 public int getdefaultresid() {
  return defaultresid;
 }

 /**
  * 设置默认图片资源id
  *
  * @param defaultresid
  */
 public void setdefaultresid(int defaultresid) {
  this.defaultresid = defaultresid;
 }

 /**
  * 获取默认的图片bitmap对象
  *
  * @return
  */
 public bitmap getdefaultbitmap() {
  return defaultbitmap;
 }

 /**
  * 设置默认的图片bitmap对象
  *
  * @param defaultbitmap
  */
 public void setdefaultbitmap(bitmap defaultbitmap) {
  this.defaultbitmap = defaultbitmap;
 }

 /**
  * 获取处理图片显示方式的标志
  *
  * @return
  */
 public int getimagehandlerwhat() {
  return imagehandlerwhat;
 }

 /**
  * 设置处理图片显示方式的标志
  *
  * @return
  */
 public void setimagehandlerwhat(int imagehandlerwhat) {
  this.imagehandlerwhat = imagehandlerwhat;
 }

 /**
  * 获取图片参数
  *
  * @return
  */
 public imageoptions getimageoptions() {
  return imageoptions;
 }

 /**
  * 设置图片参数
  *
  * @param imageoptions
  */
 public void setimageoptions(imageoptions imageoptions) {
  this.imageoptions = imageoptions;
 }

 /**
  * 获取图片形状接口
  *
  * @return
  */
 public imagetransform getimagetransform() {
  return imagetransform;
 }

 /**
  * 设置图片形状接口
  *
  * @param imagetransform
  */
 public void setimagetransform(imagetransform imagetransform) {
  this.imagetransform = imagetransform;
 }

 public context getcontext() {
  return context;
 }

 public void setcontext(context context) {
  this.context = context;
 }
}

imagetask (图片下载任务类)

该类实现了对图片的下载任务继承runable方便线程池管理和线程启动,此类是一个抽象类,如果用户觉得okhttp网络框架和自己项目违背,那么只需要继承该类,实现downlod()方法就行了,在download()方法中写自己网络下载图片的方式就ok!

import android.graphics.bitmap;
import android.os.environment;
import android.os.message;

import com.android.image.gifdrawable;
import com.android.image.transforms.imagetransform;

import java.io.file;
import java.io.ioexception;

import static android.os.environment.directory_documents;

/**
 * created by relin
 * on 2018-06-28.
 * 图片下载任务基本类
 * 在不修改下载方式的情况下,可以不管
 * 如果你需要修改下载的网络访问方式,请集成改类
 * 在downlod()方法中写下载的文件就行了,其余的逻辑
 * 完全不用你管。
 */

public abstract class imagetask implements runnable {

 /**
  * 图片数据
  */
 private imageholder holder;
 /**
  * 图片最终处理
  */
 private imagehandler handler;

 /**
  * 构造方法
  *
  * @param holder  图片对象类
  * @param handler 图片处理类
  */
 public imagetask(imageholder holder, imagehandler handler) {
  this.holder = holder;
  this.handler = handler;
  createcachefolder(null);
 }

 /**
  * 构造方法
  *
  * @param holder图片对象类
  * @param handler  图片处理类
  * @param cachefolder 缓存文件夹-带路径的
  */
 public imagetask(imageholder holder, imagehandler handler, string cachefolder) {
  this.holder = holder;
  this.handler = handler;
  createcachefolder(cachefolder);
 }

 /**
  * 创建缓存文件夹
  *
  * @param folder
  */
 private void createcachefolder(string folder) {
  file file = new file(folder == null  (environment.getexternalstoragepublicdirectory(directory_documents).getparent() + file.separator + "ximageloader") : folder);
  if (!file.exists()) {
file.mkdirs();
  }
  holder.setcachefolder(file.getabsolutepath());
 }

 /**
  * 通过url获取文件名称
  *
  * @param url
  * @return
  */
 private string namefromurl(string url) {
  return url.substring(url.lastindexof("/") + 1);
 }

 @override
 public void run() {
  //请求信号量
  holder.acquiresemaphore();
  //通过数据类获取对应的资源路径、缓存文件夹、加载图片的类型、图片参数
  string url = holder.geturl();
  string folder = holder.getcachefolder();
  int what = holder.getimagehandlerwhat();
  imageoptions options = holder.getimageoptions();
  imagetransform transform = holder.getimagetransform();
  //操作处理对应类型的图片
  switch (what) {
case imagehandler.show_internet_image:
 dohttpimage(url, folder, options, transform);
 break;
case imagehandler.show_local_image:
 dolocalimage(url, options, transform);
 break;
case imagehandler.show_assets_image:
 doassetsimage(url, folder, options, transform);
 break;
case imagehandler.show_gif_image:
 doassetsimage(url, folder, options, transform);
 break;
  }
  sendmsgshowimage(what);
 }

 /**
  * 实现对图片的下载操作
  *
  * @param url  图片网络地址
  * @param file 图片缓存的地址
  */
 protected abstract void download(string url, file file);

 /**
  * 处理网络图片
  *
  * @param url  网络地址
  * @param folder  缓存文件夹
  * @param options 图片参数
  */
 private void dohttpimage(string url, string folder, imageoptions options, imagetransform transform) {
  //储存下载文件的目录
  file folderfile = new file(folder);
  if (!folderfile.exists()) {
folderfile.mkdirs();
  }
  file file = new file(holder.getcachefolder(), namefromurl(holder.geturl()));
  if (!file.exists()) {
try {
 file.createnewfile();
} catch (ioexception e) {
 e.printstacktrace();
}
download(url, file);
  }
  holder.setfile(file);
  if (imageutils.isgif(url)) {
dogifimage(url, file);
  } else {
bitmap bitmap = imageutils.decodesampledbitmap(file, options.getwidth(), options.getheight());
if (transform != null) {
 bitmap = transform.ondrawbitmap(options.getrect(), bitmap);
}
imagecache.getinstance().addcache(url, bitmap);
  }
 }

 /**
  * 处理本地图片
  *
  * @param path 图片路径
  * @param options 图片参数
  */
 private void dolocalimage(string path, imageoptions options, imagetransform transform) {
  file file = new file(path);
  holder.setfile(file);
  if (imageutils.isgif(path)) {
dogifimage(path, file);
  } else {
bitmap localbitmap = imageutils.decodesampledbitmap(new file(path), options.getwidth(), options.getheight());
if (transform != null) {
 localbitmap = transform.ondrawbitmap(options.getrect(), localbitmap);
}
imagecache.getinstance().addcache(path, localbitmap);
  }
 }

 /**
  * 处理assets文件
  *
  * @param name assets文件名: 例如ic_test.png
  * @param folder  缓存文件夹,此处的用做拷贝数据到缓存文件夹
  * @param options 图片参数
  */
 private void doassetsimage(string name, string folder, imageoptions options, imagetransform transform) {
  file assetsfile = imageutils.decodeassetsbitmap(folder, name);
  holder.setfile(assetsfile);
  if (imageutils.isgif(name)) {
dogifimage(name, assetsfile);
return;
  }
  bitmap assetsbitmap = imageutils.decodesampledbitmap(assetsfile, options.getwidth(), options.getheight());
  if (transform != null) {
assetsbitmap = transform.ondrawbitmap(options.getrect(), assetsbitmap);
  }
  imagecache.getinstance().addcache(name, assetsbitmap);
 }

 private void dogifimage(string url, file file) {
  try {
gifdrawable gifdrawable = new gifdrawable(file);
imagecache.getinstance().addgifcache(url, gifdrawable);
  } catch (ioexception e) {
e.printstacktrace();
  }
 }

 /**
  * 显示图片
  *
  * @param what 图片类型
  */
 protected void sendmsgshowimage(int what) {
  message msg = handler.obtainmessage();
  msg.what = what;
  msg.obj = holder;
  handler.sendmessage(msg);
 }

}

imagecache(图片缓存)

图片缓存主要是继承android的类,实现对bitmap和gifdrawable的缓存,给图片加载节省时间的同时防止oom发生。

import android.graphics.bitmap;
import android.util.lrucache;

import com.android.image.gifdrawable;

/**
 * created by relin
 * on 2018-06-28.
 * 图片的缓存类,此类主要用作图片一级
 * 缓存,在考虑内存不足的情况下回oom。
 * 当然你可以使用softreference类实现,
 * 只是你是android程序员不是java后台人员,
 * 有android写好的算法你不用,那我就没办法了。
 */

public class imagecache extends lrucache {

 /**
  * 图片缓存对象
  */
 public static imagecache imagecache;

 /**
  * 获取图片缓存的单例对象
  * @return imagecache
  */
 public static imagecache getinstance() {
  if (imagecache == null) {
int maxmemory = (int) (runtime.getruntime().totalmemory() / 1024);
int cachesize = maxmemory / 8;
imagecache = new imagecache(cachesize);
  }
  return imagecache;
 }

 /**
  * @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 imagecache(int maxsize) {
  super(maxsize);
 }


 /**
  * add bitmap to cache
  *
  * @param url
  * @param bitmap
  */
 public void addcache(string url, bitmap bitmap) {
  if (url == null || bitmap == null) {
return;
  }
  put(url, bitmap);
 }

 /**
  * get cache from memory
  *
  * @param url
  * @return
  */
 public bitmap getcache(string url) {
  if (url == null) {
return null;
  }
  return (bitmap) get(url);
 }

 /**
  * add bitmap to cache
  *
  * @param url
  * @param drawable
  */
 public void addgifcache(string url, gifdrawable drawable) {
  if (url == null || drawable == null) {
return;
  }
  put(url, drawable);
 }

 /**
  * get cache from memory
  *
  * @param url
  * @return
  */
 public gifdrawable getgifcache(string url) {
  if (url == null) {
return null;
  }
  return (gifdrawable) get(url);
 }

}
imagehandler(图片显示处理)
import android.graphics.bitmap;
import android.os.handler;
import android.os.message;
import android.view.viewtreeobserver;
import android.widget.imageview;

import com.android.image.gifdrawable;
import com.android.image.gifimageview;
import com.android.image.transforms.imagetransform;

import java.io.file;
import java.io.ioexception;
import java.util.concurrent.semaphore;

/**
 * created by relin
 * on 2018-06-27.
 */

public class imagehandler extends handler {

 /**
  * 显示网络图片标志
  */
 public static final int show_internet_image = 0x002;
 /**
  * 显示本地图片标志
  */
 public static final int show_local_image = 0x001;
 /**
  * 显示assets文件夹图片标志
  */
 public static final int show_assets_image = 0x003;

 /**
  * 显示gif图片标志
  */
 public static final int show_gif_image = 0x004;


 @override
 public void handlemessage(message msg) {
  super.handlemessage(msg);
  //获取对应图片信息
  imageholder holder = (imageholder) msg.obj;
  //显示图片
  showbitmap(holder, msg);
 }

 /**
  * 显示图片
  *
  * @param holder 图片数据类
  * @param msg 显示图片的消息
  */
 private void showbitmap(imageholder holder, message msg) {
  string tag = (string) holder.gettarget().gettag();
  string url = holder.geturl();
  file file = holder.getfile();
  imageview target = holder.gettarget();
  semaphore semaphore = holder.getsemaphore();
  string folder = holder.getcachefolder();
  imageoptions options = holder.getimageoptions();
  imagetransform transform = holder.getimagetransform();
  //判断是否是对应图片的对应路径
  if (!url.equals(tag)) {
semaphore.release();
return;
  }
  if (isgif(url)) {
loadgifimage(url, target, file, transform);
semaphore.release();
return;
  } else {
bitmap cachebitmap = imagecache.getinstance().getcache(url);
if (cachebitmap != null) {
 load(target, url, file, transform, options);
 semaphore.release();
 return;
}
  }
  switch (msg.what) {
case show_internet_image://网络图片
 load(target, url, file, transform, options);
 break;
case show_local_image://本地图片
 load(target, url, file, transform, options);
 break;
case show_assets_image://assets文件夹下的图片
 load(target, url, file, transform, options);
 break;
  }
  semaphore.release();
 }

 /**
  * 是否是gif图片
  *
  * @param url
  * @return
  */
 private boolean isgif(string url) {
  return url.endswith(".gif");
 }

 /**
  * 加载gif图片
  *
  * @param url gif资源地址
  * @param target gif显示控件
  * @param filegif图片文件
  * @param transform gif图片现状接口
  */
 private void loadgifimage(string url, imageview target, file file, imagetransform transform) {
  gifdrawable drawable = imagecache.getinstance().getgifcache(url);
  if (target instanceof gifimageview) {
if (drawable != null) {
 gifimageview gifimageview = (gifimageview) target;
 gifimageview.setimagedrawable(drawable);
} else {
 try {
  drawable = new gifdrawable(file);
 } catch (ioexception e) {
  e.printstacktrace();
 }
}
if (transform != null) {
 drawable.settransform(transform);
}
gifimageview gifimageview = (gifimageview) target;
gifimageview.setimagedrawable(drawable);
  }
 }


 /**
  * 加载图片
  *
  * @param target 显示图片的view
  * @param url 图片地址
  * @param file图片文件
  * @param transform 图片形状
  * @param options图片参数
  */
 private void load(imageview target, string url, file file, imagetransform transform, imageoptions options) {

  if (isgif(url)) {
loadgifimage(url, target, file, transform);
  } else {
bitmap bitmap = imageutils.decodesampledbitmap(file, options.getwidth(), options.getheight());
//判断是否自定显示图片形状
if (transform != null) {
 bitmap = transform.ondrawbitmap(options.getrect(), bitmap);
}
//显示图片
target.setimagebitmap(bitmap);
//加入缓存
imagecache.getinstance().addcache(url, bitmap);
  }
 }

}