APP图片加载库(框架)和缓存
一、简介
在gitHub上你会发现有很多带有针对性处理优化的一些开源项目, 比如请求网络优化、 加载网络图片优化
等, 并且这些开源的项目都提供了依赖库可以使第三方app集成使用, 这些就属于第三方框架。
二、框架分类
针对开发app中的不同环节, 我们可以根据自己的需要来使用整套的或者单个第三方框架使用; 下面介绍几种
比较有些名气的第三方框架:
1,XUtils
项目git地址:https://github.com/wyouflf/xUtils
XUtils是一个整套第三方框架;xUtils包含了很多实用的android工具, 主要四大模块分别为DbUtils模块、
ViewUtils模块、HttpUtils模块以及BitmapUtils模块;
DbUtils模块:
android中的orm( 依赖注入) 框架, 一行代码就可以进行增删改查;
支持事务, 默认关闭;
可通过注解自定义表名, 列名, 外键, 唯一性约束,NOT NULL约束,CHECK约束等( 需要混
淆的时候请注解表名和列名) ;
支持绑定外键, 保存实体时外键关联实体自动保存或更新;
自动加载外键关联实体, 支持延时加载;
支持链式表达查询, 更直观的查询语义, 参考下面的介绍或sample中的例子。
ViewUtils模块:
android中的ioc框架, 完全注解方式就可以进行UI绑定和事件绑定;
新的事件绑定方式, 使用混淆工具混淆后仍可正常工作;
目前支持常用的11种事件绑定, 参见ViewCommonEventListener类和包
com.lidroid.xutils.view.annotation.event。
HttpUtils模块:
支持同步, 异步方式的请求;
支持大文件上传, 上传大文件不会oom;
支持GET,POST,PUT,MOVE,COPY,DELETE,HEAD请求;
下载支持301/302重定向, 支持设置是否根据Content-Disposition重命名下载的文件;
返回文本内容的GET请求支持缓存, 可设置默认过期时间和针对当前请求的过期时间。
BitmapUtils模块:
加载bitmap的时候无需考虑bitmap加载过程中出现的oom和android容器快速滑动时候出现的
图片错位等现象;
支持加载网络图片和本地图片;
内存管理使用lru算法, 更好的管理bitmap内存;
可配置线程加载线程数量, 缓存大小, 缓存路径, 加载显示动画等...
但是据反映使用BitmapUtils有时候会出现图片加载不出情况, 或者还是出现小几率的OutofMerry, 只能期
待xUtils继续更新优化了;
2,Volley
项目git地址:https://github.com/mcxiaoke/android-volley
Volley是2013年Google I/O大会上推出了一个新的网络通信框架;Volley可是说是把AsyncHttpClient(也
是一个网络封装框架)和Universal-Image-Loader(之前特别出名的一个加载网络图片的框架, 下面会介绍)的优
点集于了一身, 既可以像AsyncHttpClient一样非常简单地进行HTTP通信, 也可以像Universal-ImageLoader一样轻松加载网络上的图片; 它的设计目标就是非常适合去进行数据量不大, 但通信频繁的网络操作, 而
对于大数据量的网络操作, 比如说下载文件等,Volley的表现就会非常糟糕。
Volley 的优点:
自动调度网络请求;
高并发网络连接;
通过标准的HTTP cache coherence( 高速缓存一致性) 缓存磁盘和内存透明的响应;
支持指定请求的优先级;
网络请求cancel机制。 我们可以取消单个请求, 或者指定取消请求队列中的一个区域;
框架容易被定制, 例如, 定制重试或者回退功能;
包含了调试与追踪工具;
请求池, 连接池,Network池
Volley除了支持大数据下载的缺点外, 还有一个就是Volley默认不支持HTTPS, 因此如果使用Volley框架
在使用HTTPS请求的接口必须自己进行证书信任相关处理;
Https跟http区别:
https相对于http安全, ( 通过证书里面的密钥进行请求加密)
volley框架内也有对加载网络图片进行处理的ImageLoader,NetworkImagerView,setImagUrl("图片
地址");
3,Universal-Image-Loader
项目git地址:https://github.com/nostra13/Android-Universal-Image-Loader
Universal-Image-Loader是之前很多开发者最常用的图片异步加载、 缓存的开源框架之一, 由于其对加载图片
内存优化处理非常好, 所以很多app都可以发现它的影子;
原理图:
ImageLoad的使用:(1),下载git项目或者jar包;(2),AndroidManifest中配置权限;
(3),在Application中初始化;
public static voidinitImageLoader(Context context) {
ImageLoaderConfiguration.Builder config =newImageLoaderConfiguration.Builder(context);
config.threadPriority(Thread.NORM_PRIORITY-2);//设置线程池线程数
config.denyCacheImageMultipleSizesInMemory();//清空cache缓存
config.diskCacheFileNameGenerator(newMd5FileNameGenerator());//存储文件名md5加密
config.diskCacheSize(50*1024*1024);//硬盘最大缓存为50 MiB
config.tasksProcessingOrder(QueueProcessingType.LIFO);//线程为后进先出型
config.writeDebugLogs();// Remove for release app
// Initialize ImageLoader with configuration.
ImageLoader.getInstance().init(config.build());
}(4),加载网络图片;
ImageLoader imageLoader = ImageLoader.getInstance();
image
Loader.displayImage("http://pic.tuanche.com/ams/20160215/14555012465214662.png",img_test);(5),加载本地图片;
ImageLoader.getInstance().loadImage("本地路径",newSimpleImageLoadingListener() {
@Override
public voidonLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
}@
Override
public voidonLoadingFailed(String imageUri, View view, FailReason failReason) {
}
});
常用的两种框架介绍(图片处理和网络请求框架)
4,Fresco(也是我们项目中要使用的)
项目git地址:https://github.com/facebook/fresco
Fresco是facebook推出的一款强大的图片加载的框架; 主要有Image Pipeline和Drawees两大模块;
Image Pipeline模块:
Fresco中设计有一个叫做Image Pipeline的模块。 它负责从网络, 从本地文件系统, 本地资源加载图
片。 为了最大限度节省空间和CPU时间, 它含有3级缓存设计(2级内存,1级磁盘) 。
Drawees模块:
Fresco中设计有一个叫做Drawees模块, 它会在图片加载完成前显示占位图, 加载成功后自动替换为目标
图片。 当图片不再显示在屏幕上时, 它会及时地释放内存和空间占用。
Fresco特性
(1)内存管理
解压后的图片, 即Android中的Bitmap, 占用大量的内存。 大的内存占用势必引发更加频繁的GC。 在5.0以下,GC将会
显著地引发界面卡顿。
在5.0以下系统,Fresco将图片放到一个特别的内存区域。 当然, 在图片不显示的时候, 占用的内存会自动被释放。 这
会使得APP更加流畅, 减少因图片内存占用而引发的OOM。
(2)图片加载
Fresco的Image Pipeline允许你用很多种方式来自定义图片加载过程, 比如:
为同一个图片指定不同的远程路径, 或者使用已经存在本地缓存中的图片
先显示一个低清晰度的图片, 等高清图下载完之后再显示高清图
加载完成回调通知
对于本地图, 如有EXIF缩略图, 在大图加载完成之前, 可先显示缩略图
缩放或者旋转图片
对已下载的图片再次处理
(3)图片绘制
Fresco的Drawees设计, 带来一些有用的特性:
自定义居中焦点
圆角图, 当然圆圈也行
下载失败之后, 点击重现下载
自定义占位图, 自定义overlay,或者进度条
指定用户按压时的overlay
(4)图片的渐进式呈现
渐进式的JPEG图片格式已经流行数年了, 渐进式图片格式先呈现大致的图片轮廓, 然后随着图片下载的继续, 呈现
逐渐清晰的图片, 这对于移动设备, 尤其是慢网络有极大的利好, 可带来更好的用户体验。
Android本身的图片库不支持此格式, 但是Fresco支持。 使用时, 和往常一样, 仅仅需要提供一个图片的URI即可, 剩
下的事情,Fresco会处理。
(5)支持gif等动态图加载;
Fresco的使用:(1),gitHub下载依赖库或者jar包;Anroid Studio可以通过compile方式加载:
compile'com.facebook.fresco:fresco:0.8.1'
(2),添加网络权限;
(3),在Application中初始化Fresco;Fresco.initialize(this);
(3),layout布局文件中声明控件;
android:id="@+id/image_view"
android:layout_width="300dp"
android:layout_height="300dp"
fresco:placeholderImage="@mipmap/ic_launcher"/>(4),加载网络图片
Uriuri =Uri.parse("http://pic1.nipic.com/2008-09-08/200898163242920_2.jpg");
image_view.setImageURI(uri);
注: 加载本地图片把Url地址改成本地地址即可;
当然上面提到的SimpleDraweeView只是Drawee其中的控件, 没有什么很特别的需求使用它就够了, 下面贴一下它里面的一
些属性:
android:id="@+id/image_view"
android:layout_width="300dp"
android:layout_height="300dp"
fresco:fadeDuration="300"
fresco:actualImageScaleType="focusCrop"
fresco:placeholderImage="@color/wait_color"
fresco:placeholderImageScaleType="fitCenter"
fresco:failureImage="@drawable/error"
fresco:failureImageScaleType="centerInside"
fresco:retryImage="@drawable/retrying"
fresco:retryImageScaleType="centerCrop"
fresco:progressBarImage="@drawable/progress_bar"
fresco:progressBarImageScaleType="centerInside"
fresco:progressBarAutoRotateInterval="1000"
fresco:backgroundImage="@color/blue"
fresco:overlayImage="@drawable/watermark"
fresco:pressedStateOverlayImage="@color/red"
fresco:roundAsCircle="false"
fresco:roundedCornerRadius="1dp"
fresco:roundTopLeft="true"
fresco:roundTopRight="false"
fresco:roundBottomLeft="false"
fresco:roundBottomRight="true"
fresco:roundWithOverlayColor="@color/corner_color"
fresco:roundingBorderWidth="2dp"
fresco:roundingBorderColor="@color/border_color"
/>
简单介绍一下上面的属性:
placeholderImage就是所谓的展位图啦, 在图片没有加载出来之前你看到的就是它
failureIamge看到名字就知道是什么了, 图片加载失败时显示的图片就是它了
retryImage图片加载失败时显示, 提示用户点击重新加载, 重复加载4次还是没有加载出来的时候才会显示failureImage的图片
progressBarImage进度条图片
backgroundImage背景图片, 这里的背景图片首先被绘制
overlayImage设置叠加图, 在xml中只能设置一张叠加图片, 如果需要多张图片的话, 需要在java代码中设置哦
pressedStateOverlayImage设置点击状态下的叠加图, 此叠加图不能缩放
ImageScaleType这个就是各种各样的图片缩放样式了,center,centerCrop,fouseCrop,centerInside,fitCenter,fitStart,fitEnd,fitXY
剩下的属性就是对圆角的处理了;
5,Okhttp(也是我们项目中要使用的)
项目git地址:https://github.com/square/okhttp
其他下载地址:https://github.com/hongyangAndroid/okhttp-utils
Okhttp是square公司提供的一个高效、 请求快速、 多功能的开源网络框架;
Okhttp支持SPDY协议(SPDY协议在性能上对HTTP做了很大的优化) ;
Okhttp主要包含如下:
一般的get请求
一般的post请求
基于Http Post的文件上传( 类似表单)
文件下载/加载图片
上传下载的进度回调
支持取消某个请求
支持自定义Callback
支持HEAD、DELETE、PATCH、PUT(请求方式)
支持session的保持(Session主要维持用户登录状态)
支持自签名网站https的访问, 提供方法设置下证书就行
Okhttp的使用:(1),加载Okhttp依赖库;
compile'com.squareup.okhttp3:okhttp:3.4.1'(2),在Application中配置生成OkhttpClient;
OkHttpClient okHttpClient= newOkHttpClient.Builder()
// .addInterceptor(new LoggerInterceptor("TAG"))
.connectTimeout(10000L, TimeUnit.MILLISECONDS)
.readTimeout(10000L, TimeUnit.MILLISECONDS)
//其他配置
.build();
OkHttpUtils.initClient(okHttpClient);(3),GET请求
String url="http://www.csdn.net/";
OkHttpUtils
.get()
.url(url)
.addParams("username","hyman")
.addParams("password","123")
.build()
.execute(newStringCallback()
{
@Override
public voidonError(Request request, Exception e)
{ } @
Override
public voidonResponse(String response)
{ }
});
POST请求:
OkHttpUtils
.post()
.url(url)
.addParams("username","hyman")
.addParams("password","123")
.build()
.execute(callback);
概述:
图片加载的工作流(task flow)都是3级缓存的流程;图片的内存缓存一定是LruCache实现;图片下载和读取线程的调度一定是通过线程池管理
画图说明图片加载原理
Glide的使用
详情查看https://github.com/bumptech/glide
介绍:
- 专注于处理平滑滑动的图片类库
- 默认使用HttpUrlConnection下载图片
- 支持设置渐渐显示的动画
- 支持设置加载中的图片
添加依赖
compile 'com.github.bumptech.glide:glide:3.7.0'
使用Glide加载图片
Glide.with(this).load("").centerCrop()//设置从中间剪切.placeholder(R.mipmap.ic_launcher)//设置加载过程中显示的图片.error(R.mipmap.ic_launcher)//设置加载失败显示的图片.crossFade()//设置渐渐显示的效果.into(image);
Picasso的使用
详情查看https://github.com/square/picasso
介绍:
- Square开源的比较早的图片加载类库
- 自动处理adapter中的ImageView的回收时取消下载图片
- 支持加载多种来源的图片,比如网络,sd卡,res资源
- 支持设置占位图片
- 支持对图片的自定义处理
添加依赖
compile 'com.squareup.picasso:picasso:2.5.2'
使用Picasso加载图片
Picasso.with(this).load("url").placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).centerCrop().noFade()//设置不需要渐渐显示的动画效果.resize(120,120)//指定压缩参考的宽高比.into(image);
加载其他资源路径的图片
Picasso.with(context).load(R.drawable.landing_screen).into(imageView1); Picasso.with(context).load("file:///android_asset/DvpvklR.png").into(imageView2); Picasso.with(context).load(new File(...)).into(imageView3);
- 注意:如果不设置resize(120,120),则Picasso会加载整个图片,显然这样消耗的内存比较大,一般都需要指定一下,而Glide内部已经默认参考了控件的宽高来进行缩放了。
Fresco的使用
详情查看https://github.com/facebook/fresco
介绍:
- Facebook开源的专注于优化java堆内存,最大程度减少OOM
- 在Android4.4以及以下,将图片存储在Android的一块特殊的内存区域,这会让图片处理更加快速
- 支持Gif和WebP格式的图片
添加依赖
compile 'com.facebook.fresco:fresco:0.11.0'
首先初始化Fresco,一般在Application的onCreate中初始化
//先初始化Fresco Fresco.initialize(this);
使用Fresco提供的SimpleDraweeView显示图片
draweeView.setImageURI("url");
由于使用的是自定义控件加载图片,那么通过定义属性来进行设置:
<com.facebook.drawee.view.SimpleDraweeViewandroid:id="@+id/image_view"android:layout_width="300dp"android:layout_height="300dp"fresco:fadeDuration="300"fresco:actualImageScaleType="focusCrop"fresco:placeholderImage="@color/wait_color"fresco:placeholderImageScaleType="fitCenter"fresco:failureImage="@drawable/error"fresco:failureImageScaleType="centerInside"fresco:retryImage="@drawable/retrying"fresco:retryImageScaleType="centerCrop"fresco:progressBarImage="@drawable/progress_bar"fresco:progressBarImageScaleType="centerInside"fresco:progressBarAutoRotateInterval="1000"fresco:backgroundImage="@color/blue"fresco:overlayImage="@drawable/watermark"fresco:pressedStateOverlayImage="@color/red"fresco:roundAsCircle="false"fresco:roundedCornerRadius="1dp"fresco:roundTopLeft="true"fresco:roundTopRight="false"fresco:roundBottomLeft="false"fresco:roundBottomRight="true"fresco:roundWithOverlayColor="@color/corner_color"fresco:roundingBorderWidth="2dp"fresco:roundingBorderColor="@color/border_color" />属性解释: placeholderImage就是所谓的展位图啦,在图片没有加载出来之前你看到的就是它failureIamge看到名字就知道是什么了,图片加载失败时显示的图片就是它了retryImage图片加载失败时显示,提示用户点击重新加载,重复加载4次还是没有加载出来的时候才会显示failureImage的图片progressBarImage进度条图片 backgroundImage背景图片,这里的背景图片首先被绘制overlayImage设置叠加图,在xml中只能设置一张叠加图片,如果需要多张图片的话,需要在java代码中设置哦 pressedStateOverlayImage设置点击状态下的叠加图,此叠加图不能缩放ImageScaleType这个就是各种各样的图片缩放样式了,center,centerCrop,fouseCrop,centerInside,fitCenter,fitStart,fitEnd,fitXY
图片加载总结
- UniversalImageLoader:老牌优秀的图片加载类库,特点是配置项丰富,支持圆形图片效果显示以及添加图片加载动画。
- Picasso : Square公司出品。也是很早期出现的图片加载库。默认加载图片不会压缩,并且图片渲染模式是ARGB_8888,占用内存相比Glide稍微高一点,但是可以指定图片加载的宽高,便会依据图片的宽高进行缩放。
- Glide:专门为优化Picasso而生,所以API和Picasso简直一模一样。内部会自动根据图片的宽高来压缩图片,并且图片渲染模式为RGB_565,内存占用会减少一半,专门针对滑动中的图片加载有优化。和Picasso相比,推荐使用Glide。
- Fresco : Facebook公司开源的。特点是在android4.4以及以下,将图片的放入Android native的C++内存中,而不是Java堆内存,所以占用的Java堆内存很小,大大减小了程序出现OOM的几率;支持WebP和Gif显示;支持多种图片的显示配置,比如圆形。
- 重磅更新 2017-02-16
- 前言
- 主流图片加载库的对比
- Android-Universal-Image-Loader
- Picasso
- Glide
- Fresco
- 按需选择图片加载库
- 如何更好地封装图片加载库
- 为什么要封装
- 使用策略模式封装图片加载策略
- 源码地址
- 部分参考链接
- 更新
- 2016-12-09 ll You must not call setTag on a view Glide is targeting
- 2016-12-13 ll 添加loadGifWithPrepareCall方法
- 2016-12-26 ll 更新loadGifWithProgress方法
- 2017-1-10 ll 统一加载图片进度回调方法为loadImageWithProgress弃用并删除loadGifWithProgress方法
- 2016-12-26 ll 自定义GlideModule 并将 Glide与okhttp3集成
- 2017-1-6 ll GIF帧显示不完全 2017-1-10补充说明PS
- 2017-1-10补充说明PS
- 2017-1-6 ll You cannot start a load for a destroyed activity
- 2017-1-10 ll 简单说说图片适配的问题
- 2017-1-10 ll 添加saveImage方法实现图片的本地自定义保存功能
重磅更新 || 2017-02-16
使用ImageLoaderUtil实现一个真正意义的图集功能,持续完善和更新中
Gallery
Gallery
Gallery
重要的东西贴三遍!
前言
- 图片加载是Android开发中最最基础的功能,为了降低开发周期和难度,我们经常会选用一些图片加载的开源库
- 选取第三方SDK需要谨慎
- 二次封装
注意:所有改动更新会同步到GitHub
主流图片加载库的对比
- 共同点
- 使用简单:一句话实现图片的获取和显示
- 可配置性高:可配置各种解码、缓存、下载机制
- 自适应程度高:根据系统性能调整配置策略(如CPU核数决定最大并发数、内存决定内存缓存大小、网络状态变化调整最大并发数)
- 多级缓存
- 支持多种数据源
- 支持多种Displayer
- 兼容性好(可以配合okhttp等库进行使用)
Android-Universal-Image-Loader
- 简介
- 作者:nostra13
- 面世时间:2011
- star数(截止到发稿):14509
- https://github.com/nostra13/Android-Universal-Image-Loader
- 优点
- 支持下载进度监听(ImageLoadingListener)
- 可在View滚动中暂停图片加载(PauseOnScrollListener)
- 默认实现多种内存缓存算法(最大最先删除,使用最少最先删除,最近最少使用,先进先删除,当然自己也可以配置缓存算法)
- 缺点
- 从2015.11.27之后不再维护,项目中不建议使用
Picasso
- 简介
- 作者:JakeWharton(Square)
- 面世时间:2012
- star数(截止到发稿):12076
- https://github.com/square/picasso
- 优点
- 包较小(100k)
- 取消不在视野范围内图片资源的加载
- 使用最少的内存完成复杂的图片转换
- 自动添加二级缓存
- 任务调度优先级处理
- 并发线程数根据网络类型调整
- 图片的本地缓存交给同为Square出品的okhttp处理,控制图片的过期时间
- 缺点
- 功能较为简单
- 自身无实现“本地缓存”
Glide
- 简介
- 作者:Sam sjudd (Google)
- 面世时间:2013
- star数(截止到发稿):12067
- https://github.com/bumptech/glide
- 优点
- 多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
- 生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)
- 高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)
- 高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)
- 缺点
- 方法较多较复杂,因为相当于在Picasso上的改进,包较大(500k),影响不是很大
Fresco
- 简介
- 作者:Facebook
- 面世时间:2015
- star数(截止到发稿):11235
- https://github.com/facebook/fresco
- 优点
- 最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem区)
- 大大减少OOM(在更底层的Native层对OOM进行处理,图片将不再占用App的内存)
- 适用于需要高性能加载大量图片的场景
- 缺点
- 包较大(2~3M)
- 用法复杂
- 底层涉及c++领域,阅读源码深入学习难度大
按需选择图片加载库
- 图片加载需要支持Gif,之前项目中使用的Android-Universal-Image-Loader不支持Gif且Android-Universal-Image-Loader已经停止维护,遂决定替换图片加载库
分析完优缺点最终选择Glide的其它理由:
- Glide是在Picasso的基础上进行改进的(支持Gif,内存开销小),虽然500k左右的包大小相对于Picasso较大,但是这个数量级的影响可以接受
- 初衷是想一直维持图片的原始ImageView,而 Fresco需要在布局文件中将图片控件声明为库中自定义的SimpleDraweeView,如果切库还需要更改组件,代价会很高
- Google推荐(亲儿子),在Google很多开源项目中广泛使用
但不可避免的是,Glide在使用的过程中依然存在着许多坑需要我们去填!
如何更好地封装图片加载库
为什么要封装?
先从现在面对的情形来看,项目中使用图片加载的地方都是使用的类似下面的语句
ImageLoader.getInstance().displayImage(imageUrl, imageView,options);
- 1
- 1
然而现在ImageLoader已经停止维护且已经无法满足项目需求,我们需要替换,这时你会发现如果换库的话,所有涉及到的地方都要修改(Android-Universal-Image-Loader已经和图片加载的业务逻辑严重地耦合在一起了),工作量可见一斑,这就是不封装在切库时面临的窘境!
那怎么解决那?
计算机史上有个万能的解决方案就是,如果原有层面解决不了问题,那么就请再加一层!
/*** Created by soulrelay on 2016/10/11 13:42.* Class Note:* use this class to load image,single instance*/
public class ImageLoaderUtil {//图片默认加载类型 以后有可能有多种类型public static final int PIC_DEFAULT_TYPE = 0;//图片默认加载策略 以后有可能有多种图片加载策略public static final int LOAD_STRATEGY_DEFAULT = 0;private static ImageLoaderUtil mInstance;private BaseImageLoaderStrategy mStrategy;public ImageLoaderUtil() {mStrategy = new GlideImageLoaderStrategy();}//单例模式,节省资源public static ImageLoaderUtil getInstance() {if (mInstance == null) {synchronized (ImageLoaderUtil.class) {if (mInstance == null) {mInstance = new ImageLoaderUtil();return mInstance;}}}return mInstance;}/*** 统一使用App context* 可能带来的问题:http://stackoverflow.com/questions/31964737/glide-image-loading-with-application-context** @param url* @param placeholder* @param imageView*/public void loadImage(String url, int placeholder, ImageView imageView) {mStrategy.loadImage(imageView.getContext(), url, placeholder, imageView);}public void loadGifImage(String url, int placeholder, ImageView imageView) {mStrategy.loadGifImage(url, placeholder, imageView);}public void loadImage(String url, ImageView imageView) {mStrategy.loadImage(url, imageView);}/*** 展示图片加载进度*/public void loadImageWithProgress(String url, ImageView imageView, ProgressLoadListener listener) {mStrategy.loadImageWithProgress(url,imageView,listener);}public void loadGifWithProgress(String url, ImageView imageView, ProgressLoadListener listener) {mStrategy.loadGifWithProgress(url,imageView,listener);}/*** 策略模式的注入操作** @param strategy*/public void setLoadImgStrategy(BaseImageLoaderStrategy strategy) {mStrategy = strategy;}/*** 清除图片磁盘缓存*/public void clearImageDiskCache(final Context context) {mStrategy.clearImageDiskCache(context);}/*** 清除图片内存缓存*/public void clearImageMemoryCache(Context context) {mStrategy.clearImageMemoryCache(context);}/*** 根据不同的内存状态,来响应不同的内存释放策略** @param context* @param level*/public void trimMemory(Context context, int level) {mStrategy.trimMemory(context, level);}/*** 清除图片所有缓存*/public void clearImageAllCache(Context context) {clearImageDiskCache(context.getApplicationContext());clearImageMemoryCache(context.getApplicationContext());}/*** 获取缓存大小** @return CacheSize*/public String getCacheSize(Context context) {return mStrategy.getCacheSize(context);}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
所有需要图片显示的地方使用如下方法进行调用:
- 入口唯一,所有图片加载都在ImageLoaderUtil这一个地方统一管理,使用了单例模式(据说单元素的枚举类型已经成为实现Singleton的最佳方法,你可以试试 ),
- 高效地封装减少了切库(只需要切换图片加载策略)带来的代价,默认采用GlideImageLoaderStrategy
总结:外部表现一致,内部灵活处理原则。
/*** 图片加载库的封装演示案例* Created by soulrelay on 2016/12/11 19:18*/
public class MainActivity extends AppCompatActivity {@BindView(R.id.iv_normal)ImageView ivNormal;@BindView(R.id.iv_gif)ImageView ivGif;@BindView(R.id.iv_gif1)ImageView ivGif1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);initView();}private void initView() {ImageLoaderUtil.getInstance().loadImage("http://image.sports.baofeng.com/25a3dbb0c99c5e48e52e60941ed230be", R.drawable.bg_default_video_common_small, ivNormal);ImageLoaderUtil.getInstance().loadImage("http://image.sports.baofeng.com/19ce5d6ac3b4fff255196f200b1d3079", R.drawable.bg_default_video_common_small, ivGif);ImageLoaderUtil.getInstance().loadGifImage("http://image.sports.baofeng.com/19ce5d6ac3b4fff255196f200b1d3079", R.drawable.bg_default_video_common_small, ivGif1);}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
效果图如下所示:
使用策略模式封装图片加载策略
如果你对策略模式不是很熟,请先参考策略模式和状态模式
首先我们需要抽象出一个图片加载的基础接口BaseImageLoaderStrategy
基本功能主要包括
- 正常加载图片
- 针对于GIF图片的特殊加载
- 加载图片的进度回调
- 清除缓存
- 获取缓存大小等
- 其它特殊需求自己封装,最好不要破坏策略模式的整体结构
/*** Created by soulrelay on 2016/10/11.* Class Note:* abstract class/interface defined to load image* (Strategy Pattern used here)*/
public interface BaseImageLoaderStrategy {//无占位图void loadImage(String url, ImageView imageView);void loadImage(String url, int placeholder, ImageView imageView);void loadImage(Context context, String url, int placeholder, ImageView imageView);void loadGifImage(String url, int placeholder, ImageView imageView);void loadImageWithProgress(String url, ImageView imageView, ProgressLoadListener listener);void loadGifWithProgress(String url, ImageView imageView, ProgressLoadListener listener);//清除硬盘缓存void clearImageDiskCache(final Context context);//清除内存缓存void clearImageMemoryCache(Context context);//根据不同的内存状态,来响应不同的内存释放策略void trimMemory(Context context, int level);//获取缓存大小String getCacheSize(Context context);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
需要说明的一点是:
- 当封装的方法参数比较少时可以按照上述方式进行抽象,如果需要传递的参数较多,可以考虑使用建造者模式建造者模式
- 例如封装一个ImageLoaderConfiguration,包含如下参数等等,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
- type 图片加载的类型(大图、小图、中图)
- url 需要解析的url
- placeHolder 当没有成功加载的时候显示的图片
- imgView ImageView的实例
- loadStrategy 加载策略
- 当然这里我没有使用建造模式,考虑到目前使用的对象还不算复杂(传参比较简单),而且如果使用建造者模式有可能每次都要new一个新的对象实例,虽然开销可以接受
- 使用ImageLoaderUtil的过程中,注意内存泄露的问题(静态单例的生命周期与App一样,当一个单例的对象长久不用时,不会被垃圾收集机制回收)
然后基于每个图片库的各自方式来进行相应策略的封装,需要使用哪种策略,只需要通过ImageLoaderUtil的setLoadImgStrategy(BaseImageLoaderStrategy strategy)方法将相应的策略注入,相关类图关系如下所示:
不同的图片加载库实现不同的图片加载策略
这里只是给出Glide的图片加载策略类GlideImageLoaderStrategy作为参考
- Glide依赖v4包,且需要配置android.permission.INTERNET和android.permission.WRITE_EXTERNAL_STORAGE(忘记配置权限,图片加载不出来,还看不出什么异常)
- 其中部分方法使用到了RequestListener的回调(这里是因为项目中的一些特殊需求而添加,如统计图片首次加载时长来测试一下图片cdn服务器的速度等)
- 在使用Glide的过程中遇到了一些问题,部分已经在注释中说明
- 之所以针对gif单独封装,是因为在使用的过程中发现,当在列表中加载大量gif会有OOM的问题,所以通过asGif进行特殊标明,即使这样也会出现类似问题,同时暂时通过skipMemoryCache(true)跳过内存缓存,之后有更好的办法会继续补充,各位看官如有良策,希望可以不吝赐教
- Glide本身不提供图片的progress回调,所以关于进度回调的解决方案参照的是
ProgressGlide,并做了些许改动集成到项目中- 期间发现了一个很好的问题Android的App中线程池的使用,具体使用多少个线程池?,其中一个答主的关于图片加载库线程池策略的分析很好,值得体会,简单摘录如下:
- UIL的线程池处理非常简单粗暴,没有根据CPU数量来选择,也没有根据网络状况的变化进行调整;
- Picasso的线程池会根据网络状况的变化进行调整,在Wifi下线程数为4,而4G下线程数为3, 3G下为2, 2G下为1,默认状况为3;
- Glide加载缓存未命中的线程池会根据根据CPU的数量和Java虚拟机中可用的处理器数量来选择合适的线程数,但是最多不超过4;而加载缓存命中的图片的线程池默认大小为1.
/*** Created by soulrelay on 2016/10/11 13:48.* Class Note:* using {@link Glide} to load image*/
public class GlideImageLoaderStrategy implements BaseImageLoaderStrategy {@Overridepublic void loadImage(String url, int placeholder, ImageView imageView) {loadNormal(imageView.getContext(), url, placeholder, imageView);}@Overridepublic void loadImage(Context context, String url, int placeholder, ImageView imageView) {loadNormal(context, url, placeholder, imageView);}/*** 无holder的gif加载** @param url* @param imageView*/@Overridepublic void loadImage(String url, ImageView imageView) {Glide.with(imageView.getContext()).load(url).dontAnimate().placeholder(imageView.getDrawable()).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView);}@Overridepublic void loadGifImage(String url, int placeholder, ImageView imageView) {loadGif(imageView.getContext(), url, placeholder, imageView);}@Overridepublic void loadImageWithProgress(String url, final ImageView imageView, final ProgressLoadListener listener) {Glide.with(imageView.getContext()).using(new ProgressModelLoader(new ProgressUIListener() {@Overridepublic void update(final int bytesRead, final int contentLength) {imageView.post(new Runnable() {@Overridepublic void run() {listener.update(bytesRead, contentLength);}});}})).load(url).asBitmap().dontAnimate().listener(new RequestListener<Object, Bitmap>() {@Overridepublic boolean onException(Exception e, Object model, Target<Bitmap> target, boolean isFirstResource) {listener.onException();return false;}@Overridepublic boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {listener.onResourceReady();return false;}}).into(imageView);}@Overridepublic void loadGifWithProgress(String url, final ImageView imageView, final ProgressLoadListener listener) {Glide.with(imageView.getContext()).using(new ProgressModelLoader(new ProgressUIListener() {@Overridepublic void update(final int bytesRead, final int contentLength) {imageView.post(new Runnable() {@Overridepublic void run() {listener.update(bytesRead, contentLength);}});}})).load(url).asGif().skipMemoryCache(true).dontAnimate().listener(new RequestListener<String, GifDrawable>() {@Overridepublic boolean onException(Exception e, String model, Target<GifDrawable> target, boolean isFirstResource) {listener.onException();return false;}@Overridepublic boolean onResourceReady(GifDrawable resource, String model, Target<GifDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {listener.onResourceReady();return false;}}).into(imageView);}@Overridepublic void clearImageDiskCache(final Context context) {try {if (Looper.myLooper() == Looper.getMainLooper()) {new Thread(new Runnable() {@Overridepublic void run() {Glide.get(context.getApplicationContext()).clearDiskCache();}}).start();} else {Glide.get(context.getApplicationContext()).clearDiskCache();}} catch (Exception e) {e.printStackTrace();}}@Overridepublic void clearImageMemoryCache(Context context) {try {if (Looper.myLooper() == Looper.getMainLooper()) { //只能在主线程执行Glide.get(context.getApplicationContext()).clearMemory();}} catch (Exception e) {e.printStackTrace();}}@Overridepublic void trimMemory(Context context, int level) {Glide.get(context).trimMemory(level);}@Overridepublic String getCacheSize(Context context) {try {return CommonUtils.getFormatSize(CommonUtils.getFolderSize(Glide.getPhotoCacheDir(context.getApplicationContext())));} catch (Exception e) {e.printStackTrace();}return "";}/*** load image with Glide*/private void loadNormal(final Context ctx, final String url, int placeholder, ImageView imageView) {/*** 为其添加缓存策略,其中缓存策略可以为:Source及None,None及为不缓存,Source缓存原型.如果为ALL和Result就不行.然后几个issue的连接:https://github.com/bumptech/glide/issues/513https://github.com/bumptech/glide/issues/281https://github.com/bumptech/glide/issues/600modified by xuqiang*///去掉动画 解决与CircleImageView冲突的问题 这个只是其中的一个解决方案//使用SOURCE 图片load结束再显示而不是先显示缩略图再显示最终的图片(导致图片大小不一致变化)final long startTime = System.currentTimeMillis();Glide.with(ctx).load(url).dontAnimate().placeholder(placeholder).diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener<String, GlideDrawable>() {@Overridepublic boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {return false;}@Overridepublic boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {return false;}}).into(imageView);}/*** load image with Glide*/private void loadGif(final Context ctx, String url, int placeholder, ImageView imageView) {final long startTime = System.currentTimeMillis();Glide.with(ctx).load(url).asGif().dontAnimate().placeholder(placeholder).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener<String, GifDrawable>() {@Overridepublic boolean onException(Exception e, String model, Target<GifDrawable> target, boolean isFirstResource) {return false;}@Overridepublic boolean onResourceReady(GifDrawable resource, String model, Target<GifDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {return false;}}).into(imageView);}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
源码地址
ImageLoaderUtil
部分参考链接
http://www.jianshu.com/p/97994c9693f9
https://www.zhihu.com/question/37804956
http://www.jianshu.com/p/e26130a93289
http://www.cnblogs.com/android-blogs/p/5737611.html
更新
2016-12-09 ll You must not call setTag() on a view Glide is targeting
项目中在使用Glide图片加载框架时遇到该错误
报错原因大致是因为Glide加载的iamgeView调用了setTag()方法导致的错误,因为Glide已经默认为ImageView设置的Tag
相关解决方案已经在Glide 3.6.0(issue #370)被引进,实测可行
在AndroidManifest.xml中加入
<applicationandroid:name=".App">
- 1
- 2
- 1
- 2
然后在App中添加如下代码:
public class App extends Application {@Override public void onCreate() {super.onCreate();ViewTarget.setTagId(R.id.glide_tag);}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
在src/main/values/ids.xml添加如下代码:
<resources><item type="id" name="glide_tag" />
</resources>
- 1
- 2
- 3
- 1
- 2
- 3
2016-12-13 ll 添加loadGifWithPrepareCall方法
2016.12.13
只想知道图片是否准备完毕(包括来自网络或者sdcard),区别于loadImageWithProgress和loadGifWithProgress的进度回调
Tips:使用Glide加载图片注意ImageView的Scaletype的设置
public interface BaseImageLoaderStrategy {void loadGifWithPrepareCall(String url, ImageView imageView, SourceReadyListener listener);
}
- 1
- 2
- 3
- 1
- 2
- 3
public class GlideImageLoaderStrategy implements BaseImageLoaderStrategy {@Overridepublic void loadGifWithPrepareCall(String url, ImageView imageView, final SourceReadyListener listener) {Glide.with(imageView.getContext()).load(url).asGif().dontAnimate().skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener<String, GifDrawable>() {@Overridepublic boolean onException(Exception e, String model, Target<GifDrawable> target, boolean isFirstResource) {return false;}@Overridepublic boolean onResourceReady(GifDrawable resource, String model, Target<GifDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {listener.onResourceReady(resource.getIntrinsicWidth(),resource.getIntrinsicHeight());return false;}}).into(imageView);}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
public class ImageLoaderUtil {public void loadGifWithPrepareCall(String url, ImageView imageView, SourceReadyListener listener) {mStrategy.loadGifWithPrepareCall(url,imageView,listener);}
}
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
2016-12-26 ll 更新loadGifWithProgress方法
2017-1-10 ll 统一加载图片进度回调方法为loadImageWithProgress,弃用并删除loadGifWithProgress方法
具体细节查看GitHub最新代码
2016-12-26 ll 自定义GlideModule 并将 Glide与okhttp3集成
1.自定义一个GlideModule
/*** DES:自定义一个GlideModule* <p>* GlideModule 是一个抽象方法,全局改变 Glide 行为的一个方式,* 通过全局GlideModule 配置Glide,用GlideBuilder设置选项,用Glide注册ModelLoader等。* <p>*/
public class MyGlideModule implements GlideModule {@Overridepublic void applyOptions(Context context, GlideBuilder builder) {// Apply options to the builder here.int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取系统分配给应用的总内存大小int memoryCacheSize = maxMemory / 8;//设置图片内存缓存占用八分之一//设置内存缓存大小builder.setMemoryCache(new LruResourceCache(memoryCacheSize));builder.setBitmapPool(new LruBitmapPool(memoryCacheSize));}@Overridepublic void registerComponents(Context context, Glide glide) {// register ModelLoaders here.}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
2.AndroidManifest.xml注册
<manifest ...><!-- ... permissions --><application ...><!-- 自定义GlideModule --><meta-data
android:name="com.baofeng.soulrelay.utils.imageloader.MyGlideModule"android:value="GlideModule" /><!-- 自定义GlideModule --><!-- ... activities and other components --></application>
</manifest>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
3、 Glide与OkHttp3集成
compile 'com.squareup.okhttp3:okhttp:3.4.2'compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
- 1
- 2
- 1
- 2
4、添加混淆处理
#--------------------Glide-----------------------#
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {**[] $VALUES;public *;
}-keepnames class com.baofeng.soulrelay.utils.imageloader.MyGlideModule
# or more generally:
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep class com.bumptech.glide.integration.okhttp3.OkHttpGlideModule#--------------------Glide-----------------------#
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
2017-1-6 ll GIF帧显示不完全 2017-1-10补充说明PS
相关问题在issues1649中被提到和解决(目前glide:3.7.0的确存在这个问题)
具体解决方法是:
glide:3.8.0-SNAPSHOT修复了关于GIF展示的一些bug,实测可用
Gradle配置修改如下:
- Add the snapshot repo to your list of repositories:
repositories {jcenter()maven {name 'glide-snapshot'url 'http://oss.sonatype.org/content/repositories/snapshots'}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- And then change your dependencies to the v3 snapshot version:
dependencies {compile 'com.github.bumptech.glide:glide:3.8.0-SNAPSHOT'compile 'com.github.bumptech.glide:okhttp-integration:1.5.0-SNAPSHOT'
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
2017-1-10补充说明PS
PS:提供一个gif图 帧提取工具GIFFrame.exe
据我分析,那些没有显示完整的GIF图片,里面的部分帧图片本身就不是完整的,但是之前的Glide并没有做很好的处理,所以显示效果有缺陷,当然最新的3.8.0-SNAPSHOT解决了这个问题,但是在显示的时候仍有瑕疵(有一些重叠,当然我觉得这也跟gif图的做工有关)
2017-1-6 ll You cannot start a load for a destroyed activity
完整异常信息:
FATAL EXCEPTION: main
Process: com.sports.baofeng, PID: 9170
java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity
at com.bumptech.glide.d.k.b(SourceFile:134)
at com.bumptech.glide.d.k.a(SourceFile:102)
at com.bumptech.glide.d.k.a(SourceFile:87)
at com.bumptech.glide.i.c(SourceFile:629)
at com.storm.durian.common.utils.imageloader.b.a(SourceFile:1194)
at com.storm.durian.common.utils.imageloader.c.a(SourceFile:52)
at com.sports.baofeng.specialtopic.SpecialTopicDetailFixActivity.a(SourceFile:311)
at com.sports.baofeng.specialtopic.SpecialTopicDetailFixActivity.a(SourceFile:1347)
at com.sports.baofeng.specialtopic.d.a(SourceFile:1052)
at com.sports.baofeng.specialtopic.c$1.a(SourceFile:1064)
at com.storm.durian.common.b.b$1.onPostExecute(SourceFile:57)
at android.os.AsyncTask.finish(AsyncTask.java:651)
at android.os.AsyncTask.access$500(AsyncTask.java:180)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:668)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7225)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
以上异常出现的几率为random
初步分析原因是:进入页面然后又迅速退出,导致AsyncTask中的onPostExecute在调用Glide加载图片时出现如上异常,当然也跟对AsyncTask的管理有关
同样的问题参考issues138
简单的摘要:
you fire an async task and then finish() in which case you just need to pass getApplicationContext instead of this when creating the callback/asynctask
按照上面这个理解的话,如果是在AsyncTask中的onPostExecute执行时
调用Glide加载图片,context最好使用ApplicationContext
对ImageLoaderUtil做如下更新,添加方法
- BaseImageLoaderStrategy
//这里的context指定为ApplicationContextvoid loadImageWithAppCxt(String url, ImageView imageView);
- 1
- 2
- 1
- 2
- GlideImageLoaderStrategy
@Overridepublic void loadImageWithAppCxt(String url, ImageView imageView) {Glide.with(imageView.getContext().getApplicationContext()).load(url).dontAnimate().placeholder(imageView.getDrawable()).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- ImageLoaderUtil
public void loadImageWithAppCxt(String url, ImageView imageView) {mStrategy.loadImageWithAppCxt(url,imageView);}
- 1
- 2
- 3
- 1
- 2
- 3
2017-1-10 ll 简单说说图片适配的问题
过多的概念不赘述,可以先参考Android屏幕适配全攻略(最权威的官方适配指导)
这里主要描述一种现象,明白的话自然觉得很简单!
<ImageView
android:id="@+id/iv_gif"android:layout_width="wrap_content"android:layout_height="wrap_content"android:scaleType="centerInside"/>
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
假设现在要加载一张200px * 200px的GIF图片(图片基于1280 * 720),这张图片的宽高设置为wrap_content,如果在1920 * 1080分辨率的手机上显示,相对于1280 * 720(假设屏幕尺寸相同),在视觉效果上会显得小,这其实是Android系统基于手机像素密度的一种自适配,单一变化条件下,1920 * 1080分辨率的手机的像素密度是1280 * 720的1.5倍
假设如果系统的自适配让你觉得在高分辨率手机上显得图片过小(像素密度高,200个像素显示起来就比较挤),可以通过自己的计算来改变这种现象
ImageLoaderUtil提供如下加载成功回调的方法(并且会把图片的宽高告诉你):这里有个参数设置,看需求来计算,粗略点可以只使用宽度比例来算,如下面的例子显示,参数为AppParams.screenWidth / 720,当然也可以获取屏幕密度,1920 * 1080的屏幕密度为3,1280 * 720的为2,所以参数可以设置为AppParams.density/2(在两种分辨率上看着视觉上一样)
ImageLoaderUtil.getInstance().loadGifWithPrepareCall(url, mImageView, new SourceReadyListener() {@Overridepublic void onResourceReady(int width, int height) {ViewGroup.LayoutParams params = mImageView.getLayoutParams();params.height = height * AppParams.screenWidth / 720;params.width = width * AppParams.screenWidth / 720;mImageView.setLayoutParams(params);progressBar.setVisibility(View.GONE);}});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
2017-1-10 ll 添加saveImage方法,实现图片的本地自定义保存功能
已同步到GitHub ImageLoaderUtil
- ImageLoaderUtil相关接口:
/*** @param context* @param url 图片url* @param savePath 保存路径* @param saveFileName 保存文件名* @param listener 文件保存成功与否的监听器*/public void saveImage(Context context, String url, String savePath, String saveFileName, ImageSaveListener listener) {mStrategy.saveImage(context, url, savePath, saveFileName, listener);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 在工作线程中调用示例如下:
ImageLoaderUtil.getInstance().saveImage(getActivity(), url,Environment.getExternalStorageDirectory().getAbsolutePath() + "/bfsports","bfsports" + System.currentTimeMillis(), new ImageSaveListener() {@Overridepublic void onSaveSuccess() {handler.obtainMessage(MSG_PIC_SAVE_SUCC).sendToTarget();}@Overridepublic void onSaveFail() {handler.obtainMessage(MSG_PIC_SAVE_FAIL).sendToTarget();}});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
APP图片加载库(框架)和缓存相关推荐
- Android图片加载库—Picasso一个强大的图像下载和缓存库
介绍 GitHub源码 点击查看 Picasso是一款强大的图片下载和缓存开源软件,只能在Android平台上使用,由Square开发.使用Picasso可以添加一些必须的特性和视觉效果到Androi ...
- Android开源框架——图片加载库Glide
Glide是有google开发的图片加载库,支持图片加载与处理,包括动态图片的加载,以及视频的解码. 开源地址:https://github.com/bumptech/glide build.grad ...
- Coil - Google推荐的协程图片加载库
随着Kotlin的转正,Glide不再是最佳选择.看一下Google极力推荐的Coil框架. GitHub:Coilhttps://coil-kt.github.io/coil/ Pangu-Immo ...
- Android图片加载库的封装实战
重磅更新 2017-02-16 2017-05-09 优化圆形图片加载 更新demo 前言 主流图片加载库的对比 Android-Universal-Image-Loader Picasso Glid ...
- android图片加载库的使用对比
1.Android-Universal-Image-Loader giehub地址::https://github.com/nostra13/Android-Universal-Image-Loade ...
- FaceBook推出的Android图片加载库-Fresco
源文件:http://www.mamicode.com/info-detail-544084.html 在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像 ...
- Android图片加载库Fresco
在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像这方面遇到了很多问题.图片太大,但是手机的内存却很小.每一个像素的R.G.B和alpha通道总共要占用4 ...
- fackbook的Fresco (FaceBook推出的Android图片加载库-Fresco)
[Android开发经验]FaceBook推出的Android图片加载库-Fresco 欢迎关注ndroid-tech-frontier开源项目,定期翻译国外Android优质的技术.开源库.软件架构 ...
- FaceBook推出的Android图片加载库Fresco
在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像这方面遇到了很多问题.图片太大,但是手机的内存却很小.每一个像素的R.G.B和alpha通道总共要占用4 ...
- Fresco-FaceBook推出的Android图片加载库
在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像这方面遇到了很多问题.图片太大,但是手机的内存却很小.每一个像素的R.G.B和alpha通道总共要占用4 ...
最新文章
- WPF 4 Ribbon 开发 之 应用程序菜单(Application Menu)
- 两个整数相加减是否溢出
- 在控制台程序中隐藏控制台窗口
- java传参怎么理解_如何理解Java的值传递
- wget: command not found
- 08TensorFlow2.0基础--8.6tensoflow-gpu和cpu
- Diagrams for Mac(原生流程图制作工具)
- this绑定丢失的问题
- PMP笔记 第6章 项目进度管理
- 计算标准累积正态分布_神说要有正态分布,于是就有了正态分布。
- 电信怎么关闭信息接受服务器,怎么关闭短信接收功能
- 【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )
- John McCarthy:人工智能之父
- c语言的所有头文件,C语言所有头文件.doc
- 中鑫优配谨防黑周四出现这种走势!
- Android——加速传感器(ACCELEROMETER)的应用
- python爬取微博热门消息(一)——效果展示
- 范畴(Category)
- NVIDIA支持CUDA的显卡选型简述
- pli测试50题题库_马士基笔试题目
热门文章
- Windows11 安装 WSA 简单上手一试
- 最简单!阿里云服务器采用 LNMP一键安装包 配置 Linux+Nginx+Mysql+PHP
- Linux搭建FTP服务器
- MySQL5.7官网参考手册路径
- 步进电机驱动器细分原理_步进驱动器细分设置表说明
- 2021最全最新java学习指南(第1-5节),干货必须分享!
- python爬虫框架论文开题报告范文_基于Web爬虫系统设计开题报告
- 记录一次破解某加固APP的修改纪录
- UI - PS如何导入不同的字体
- 微星z370黑苹果_[原创]黑苹果Hackintosh 10.13.5 High Sierra i7 8700k z370