Android XImageLoader框架知识详解
1.前言
在做android也有一段时间了,在以往的习惯中或者说作为初学者的开发者来说,在项目周期很紧张的情况之下,迫于周期总是喜欢直接在网上找别人写的项目或者框架来用,但是一旦出问题总是看着别人的代码是很头痛得事情,在此种种的情况之下,萌生自己写框架的想法,这样自己维护自己的框架那么就没那么痛苦了,想到目前app项目中用得最多的也是图片异步加载,那么就决定此次对图片加载进行开刀,说实在的对于图片加载框架目前已经有很多框架了比如imageloader、picasso、glide、fresco;这几种目前是用得最多的图片加载框架了;就个人习惯总觉得有点不是很习惯吧;当然只是个人习惯哈,目前支持gif图片加载和普通图片都支持的加载glide、fresco、并且还要支持图片的gif自定义形状的估计也就少了吧;目前自己写的框架支持gif图片和普通图片加载,并且可以显示圆角、圆形图片,支持自定图片形状,其中gif图片加载对android drawable gif开源框架的基础上修改部分;支持okhttp 3.0网络框架下载,通知支持自定网络工具自定义。
2.框架设计
框架中需要储备知识点:
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); } } }
推荐阅读
-
Android网络框架的优缺点、图片加载框架等基础知识讲解
-
Android XImageLoader框架知识详解
-
Android路由框架-ARouter详解
-
Android系统自带的VPN服务框架实例详解
-
详解Android控件状态依赖框架
-
详解Android框架MVVM分析以及使用
-
JavaScript常用的命令, JQuery框架, Ajax异步IO技术等知识详解
-
Java中高级核心知识全面解析——常用框架(Spring IOC和 AOP详解)
-
Android 网络请求框架Volley实例详解
-
Android 进阶——Framework 核心 Android Storage Access Framework(SAF)存储访问框架机制详解(一)