转载自:http://blog.csdn.net/s003603u/article/details/53436089
重磅更新 || 2017-02-16
使用ImageLoaderUtil实现一个真正意义的图集功能,持续完善和更新中
Gallery
Gallery
Gallery
重要的东西贴三遍!
2017-05-09 || 优化圆形图片加载 更新demo
前言
注意:所有改动更新会同步到GitHub
主流图片加载库的对比
- 共同点
- 使用简单:一句话实现图片的获取和显示
- 可配置性高:可配置各种解码、缓存、下载机制
- 自适应程度高:根据系统性能调整配置策略(如CPU核数决定最大并发数、内存决定内存缓存大小、网络状态变化调整最大并发数)
- 多级缓存
- 支持多种数据源
- 支持多种Displayer
- 兼容性好(可以配合okhttp等库进行使用)
Android-Universal-Image-Loader
- 简介
- 优点
- 支持下载进度监听(ImageLoadingListener)
- 可在View滚动中暂停图片加载(PauseOnScrollListener)
- 默认实现多种内存缓存算法(最大最先删除,使用最少最先删除,最近最少使用,先进先删除,当然自己也可以配置缓存算法)
- 缺点
- 从2015.11.27之后不再维护,项目中不建议使用
Picasso
- 简介
- 优点
- 包较小(100k)
- 取消不在视野范围内图片资源的加载
- 使用最少的内存完成复杂的图片转换
- 自动添加二级缓存
- 任务调度优先级处理
- 并发线程数根据网络类型调整
- 图片的本地缓存交给同为Square出品的okhttp处理,控制图片的过期时间
- 缺点
Glide
- 简介
- 优点
- 多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
- 生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)
- 高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)
- 高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)
- 缺点
- 方法较多较复杂,因为相当于在Picasso上的改进,包较大(500k),影响不是很大
Fresco
- 简介
- 优点
- 最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem区)
- 大大减少OOM(在更底层的Native层对OOM进行处理,图片将不再占用App的内存)
- 适用于需要高性能加载大量图片的场景
- 缺点
- 包较大(2~3M)
- 用法复杂
- 底层涉及c++领域,阅读源码深入学习难度大
按需选择图片加载库
如何更好地封装图片加载库
为什么要封装?
先从现在面对的情形来看,项目中使用图片加载的地方都是使用的类似下面的语句
ImageLoader.getInstance().displayImage(imageUrl, imageView,options)
然而现在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
所有需要图片显示的地方使用如下方法进行调用:
- 入口唯一,所有图片加载都在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;
@Override
protected 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
效果图如下所示:
使用策略模式封装图片加载策略
如果你对策略模式不是很熟,请先参考策略模式和状态模式
首先我们需要抽象出一个图片加载的基础接口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
需要说明的一点是:
- 当封装的方法参数比较少时可以按照上述方式进行抽象,如果需要传递的参数较多,可以考虑使用建造者模式建造者模式
- 例如封装一个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 {
@Override
public void loadImage(String url, int placeholder, ImageView imageView) {
loadNormal(imageView.getContext(), url, placeholder, imageView);
}
@Override
public void loadImage(Context context, String url, int placeholder, ImageView imageView) {
loadNormal(context, url, placeholder, imageView);
}
/**
* 无holder的gif加载
*
* @param url
* @param imageView
*/
@Override
public void loadImage(String url, ImageView imageView) {
Glide.with(imageView.getContext()).load(url).dontAnimate()
.placeholder(imageView.getDrawable())
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(imageView);
}
@Override
public void loadGifImage(String url, int placeholder, ImageView imageView) {
loadGif(imageView.getContext(), url, placeholder, imageView);
}
@Override
public void loadImageWithProgress(String url, final ImageView imageView, final ProgressLoadListener listener) {
Glide.with(imageView.getContext()).using(new ProgressModelLoader(new ProgressUIListener() {
@Override
public void update(final int bytesRead, final int contentLength) {
imageView.post(new Runnable() {
@Override
public void run() {
listener.update(bytesRead, contentLength);
}
});
}
})).load(url).asBitmap().dontAnimate().
listener(new RequestListener<Object, Bitmap>() {
@Override
public boolean onException(Exception e, Object model, Target<Bitmap> target, boolean isFirstResource) {
listener.onException();
return false;
}
@Override
public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {
listener.onResourceReady();
return false;
}
}).into(imageView);
}
@Override
public void loadGifWithProgress(String url, final ImageView imageView, final ProgressLoadListener listener) {
Glide.with(imageView.getContext()).using(new ProgressModelLoader(new ProgressUIListener() {
@Override
public void update(final int bytesRead, final int contentLength) {
imageView.post(new Runnable() {
@Override
public void run() {
listener.update(bytesRead, contentLength);
}
});
}
})).load(url).asGif().skipMemoryCache(true).dontAnimate().
listener(new RequestListener<String, GifDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GifDrawable> target, boolean isFirstResource) {
listener.onException();
return false;
}
@Override
public boolean onResourceReady(GifDrawable resource, String model, Target<GifDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
listener.onResourceReady();
return false;
}
}).into(imageView);
}
@Override
public void clearImageDiskCache(final Context context) {
try {
if (Looper.myLooper() == Looper.getMainLooper()) {
new Thread(new Runnable() {
@Override
public void run() {
Glide.get(context.getApplicationContext()).clearDiskCache();
}
}).start();
} else {
Glide.get(context.getApplicationContext()).clearDiskCache();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void clearImageMemoryCache(Context context) {
try {
if (Looper.myLooper() == Looper.getMainLooper()) {
Glide.get(context.getApplicationContext()).clearMemory();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void trimMemory(Context context, int level) {
Glide.get(context).trimMemory(level);
}
@Override
public 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/513
https://github.com/bumptech/glide/issues/281
https://github.com/bumptech/glide/issues/600
modified by xuqiang
*/
final long startTime = System.currentTimeMillis();
Glide.with(ctx).load(url).dontAnimate()
.placeholder(placeholder)
.diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
return false;
}
@Override
public 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>() {
@Override
public boolean onException(Exception e, String model, Target<GifDrawable> target, boolean isFirstResource) {
return false;
}
@Override
public 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
源码地址
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中加入
<application
android:name=".App">
然后在App中添加如下代码:
public class App extends Application {
@Override public void onCreate() {
super.onCreate();
ViewTarget.setTagId(R.id.glide_tag);
}
}
在src/main/values/ids.xml添加如下代码:
<resources>
<item type="id" name="glide_tag" />
</resources>
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);
}
public class GlideImageLoaderStrategy implements BaseImageLoaderStrategy {
@Override
public 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>() {
@Override
public boolean onException(Exception e, String model, Target<GifDrawable> target, boolean isFirstResource) {
return false;
}
@Override
public 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
public class ImageLoaderUtil {
public void loadGifWithPrepareCall(String url, ImageView imageView, SourceReadyListener listener) {
mStrategy.loadGifWithPrepareCall(url,imageView,listener);
}
}
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 {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int memoryCacheSize = maxMemory / 8;
builder.setMemoryCache(new LruResourceCache(memoryCacheSize));
builder.setBitmapPool(new LruBitmapPool(memoryCacheSize));
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}
- 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 ...>
<application ...>
<meta-data
android:name="com.baofeng.soulrelay.utils.imageloader.MyGlideModule"
android:value="GlideModule" />
</application>
</manifest>
3、 Glide与OkHttp3集成
compile 'com.squareup.okhttp3:okhttp:3.4.2'
compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
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-----------------------#
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'
}
}
- 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'
}
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
以上异常出现的几率为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做如下更新,添加方法
void loadImageWithAppCxt(String url, ImageView imageView);
@Override
public void loadImageWithAppCxt(String url, ImageView imageView) {
Glide.with(imageView.getContext().getApplicationContext()).load(url).dontAnimate()
.placeholder(imageView.getDrawable())
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(imageView)
}
public void loadImageWithAppCxt(String url, ImageView imageView) {
mStrategy.loadImageWithAppCxt(url,imageView);
}
2017-1-10 ll 简单说说图片适配的问题
过多的概念不赘述,可以先参考Android屏幕适配全攻略(最权威的官方适配指导)
这里主要描述一种现象,明白的话自然觉得很简单!
<ImageView
android:id="@+id/iv_gif"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerInside"
/>
假设现在要加载一张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() {
@Override
public 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);
}
});
2017-1-10 ll 添加saveImage方法,实现图片的本地自定义保存功能
已同步到GitHub 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);
}
ImageLoaderUtil.getInstance().saveImage(getActivity(), url,
Environment.getExternalStorageDirectory().getAbsolutePath() + "/bfsports",
"bfsports" + System.currentTimeMillis(), new ImageSaveListener() {
@Override
public void onSaveSuccess() {
handler.obtainMessage(MSG_PIC_SAVE_SUCC).sendToTarget();
}
@Override
public void onSaveFail() {
handler.obtainMessage(MSG_PIC_SAVE_FAIL).sendToTarget();
}
});
2017-03-08 ll Glide圆形图片加载封装
- 之前加载圆形图片,一般都是采用自定义的CircleImageview。后来开始使用Glide加载图片,期间遇到Glide和CircleImageview使用冲突的问题(如:有的图片第一次加载的时候只显示占位图,第二次才显示正常的图片,以及CircleImageview带来的崩溃问题),当时采用了一个牺牲动画效果的解决方案
- 既然已经全面使用Glide,那么就尽量基于Glide来完成加载圆形图片的方案
- 具体方案请参考Glide圆形图片加载封装