Hi~ 我是Glide
现有图片框架介绍
1. Universal-Image-Loader(UIL)
- GitHub Watch=1468 Star=15582 Fork=6454 (截止2017-8-23)
- 最早的图片加载框架
- 去年9月份停止项目维护
2. Picasso
- GitHub Watch=956 Star=14045 Fork=3647 (截止2017-8-23)
- Square 公司的作品,理论上和Square其他库结合的会相对较好
- 只有网络库实现的缓存,可以通过头文件的Cache-Control 和 Expired控制图片是否过期
3. Fresco
- GitHub Watch=945 Star=13310 Fork=3416 (截止2017-8-23)
- Facebook公司的作品
- 3级缓存设计
- 在android 5.0以下图片不存储在Java heap,减少OOM
- JPEG图片可以在native进行resize
- 可以加载Gif和WebP格式,支持视频缩略图
- 体积过于庞大(以M来计算)
4. Glide
- GitHub Watch=914 Star=17355 Fork=3511 (截止2017-8-23)
- Google主导
- 无需初始化
- 可以加载Gif和WebP格式,支持视频缩略图
总结
1.UIL和Picasso不支持Gif,这个是个硬伤。
2.Fresco体积过于庞大,除非是图片处理主导的应用,否则无法凸显它的价值。
3.Glide可以完成Picasso的功能,而且体积也比较小。所以一般项目首选。
其他
Picasso,Glide,Fresco对比分析
Fresco 与 Picasso 、Glide 的比较
Glide例子基于4.0
配置
project.gradle
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
app.gradle
apply plugin: 'android-apt'
....
dependencies {
//glide 4.0
compile 'com.android.support:support-v4:23.1.1'
compile 'com.github.bumptech.glide:glide:4.0.0'
apt 'com.github.bumptech.glide:compiler:4.0.0'
}
基本用法
1、无需初始化
2、使用流接口
3、完整的请求需要3个参数
Glide.with(this)//获取上下文环境
.load(url)//图片地址
.into(imageView1);//图片显示的地方
加载其他来源的图片
1、资源文件
Glide.with(this).load(R.drawable.hsk1).into(imageView1);
2、本地图片
File file = new File("图片地址");
Glide.with(this).load(file).into(imageView1);
3、通过Uri方式
Uri uri = Uri.parse(url);
Glide.with(this).load(uri).into(imageView1);
占位符
GlideApp
.with(this)
.load(url)
.placeholder(R.drawable.image320)//加载的时候占位
.error(new ColorDrawable(Color.BLUE))//请求资源失败的时候
.fallback(new ColorDrawable(Color.CYAN))//当请求内容为null的时候显示
.into(imageView2);
GlideApp
在4.0中不用像3.X需要在AndroidManifest.xml配置GlideModule,而是通过注解继承AppGlideModule的子类来配置。
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}
使用的时候用GlideApp.with() 代替 Glide.with()
GlideApp
.with(fragment)
.load(myUrl)
.into(imageView)
如果GlideApp没有生成
1.检查一下配置文件
2.clean工程,build工程
选择项(RequestOptions)
基本的使用
RequestOptions requestOptions = new RequestOptions();
requestOptions.placeholder(R.drawable.image320);
GlideApp.with(this).load(url)
.apply(requestOptions)
.into(imageView3);
主要功能
占位符
requestOptions.placeholder(R.drawable.image320);
requestOptions.error(new ColorDrawable(Color.BLUE));
requestOptions.fallback(new ColorDrawable(Color.CYAN));
转换
转换成圆角图片
requestOptions.transform(new RoundedCorners(20));
缓存策略
跳过内存缓存,这个默认就是false。如果不需要就设置为true来确保不会缓存到内存中
requestOptions.skipMemoryCache(false);
磁盘缓存策略
requestOptions.diskCacheStrategy(DiskCacheStrategy.ALL);
- DiskCacheStrategy.ALL:缓存所有图片
- DiskCacheStrategy.NONE:不缓存任何图片
- DiskCacheStrategy.DATA:缓存原始数据
- DiskCacheStrategy.RESOURCE:缓存转换后的数据
- DiskCacheStrategy.AUTOMATIC:自动选择存储数据
图片设置
requestOptions.encodeFormat(Bitmap.CompressFormat.WEBP);//图片格式
requestOptions.encodeQuality(90);//图片质量
requestOptions.format(DecodeFormat.PREFER_RGB_565);//图片模式
requestOptions.override(40,40);//图片限制大小
其他
requestOptions.dontTransform();//禁止转换
requestOptions.dontAnimate();//禁止动画化
转换(Transform)
图片预处理
requestOptions.centerInside();
requestOptions.centerCrop();
requestOptions.circleCrop();
requestOptions.fitCenter();
如果当没有调用transform方法并且允许转变的情况下会进行以下处理:
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
if (requestOptions.isLocked()) {
requestOptions = requestOptions.clone();
}
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions.optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions.optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions.optionalFitCenter();
break;
case FIT_XY:
requestOptions.optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
自定义模糊转换
MyBlurTransformation.class
public class MyBlurTransformation extends BitmapTransformation {
private static final String ID = "gift.witch.glide.MyBlurTransformation";
private static final byte[] ID_BYTES = ID.getBytes(CHARSET);
private static int MAX_RADIUS = 25;
private static int DEFAULT_DOWN_SAMPLING = 1;
private int mRadius;
private int mSampling;
private Context mContext;
public MyBlurTransformation(Context context) {
init(context, MAX_RADIUS, DEFAULT_DOWN_SAMPLING);
}
public MyBlurTransformation(Context context, BitmapPool pool) {
init(context, MAX_RADIUS, DEFAULT_DOWN_SAMPLING);
}
public MyBlurTransformation(Context context, BitmapPool pool, int radius) {
init(context, radius, DEFAULT_DOWN_SAMPLING);
}
public MyBlurTransformation(Context context, int radius) {
init(context, radius, DEFAULT_DOWN_SAMPLING);
}
public MyBlurTransformation(Context context, int radius, int sampling) {
init(context, radius, sampling);
}
private void init(Context context, int radius, int sampling) {
mContext = context.getApplicationContext();
mRadius = radius;
mSampling = sampling;
}
@Override
protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
Bitmap source = getAlphaSafeBitmap(pool, toTransform);
int width = source.getWidth();
int height = source.getHeight();
int scaledWidth = width / mSampling;
int scaledHeight = height / mSampling;
Bitmap bitmap = pool.get(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
if (bitmap == null) {
bitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bitmap);
canvas.scale(1 / (float) mSampling, 1 / (float) mSampling);
Paint paint = new Paint();
paint.setFlags(Paint.FILTER_BITMAP_FLAG);
canvas.drawBitmap(source, 0, 0, paint);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
try {
bitmap = RSBlur.blur(mContext, bitmap, mRadius);
} catch (RSRuntimeException e) {
bitmap = FastBlur.blur(bitmap, mRadius, true);
}
} else {
bitmap = FastBlur.blur(bitmap, mRadius, true);
}
if (!source.equals(toTransform)) {
pool.put(source);
}
return bitmap;
}
private Bitmap getAlphaSafeBitmap(@NonNull BitmapPool pool,
@NonNull Bitmap maybeAlphaSafe) {
if (Bitmap.Config.ARGB_8888.equals(maybeAlphaSafe.getConfig())) {
return maybeAlphaSafe;
}
Bitmap argbBitmap = pool.get(maybeAlphaSafe.getWidth(), maybeAlphaSafe.getHeight(),
Bitmap.Config.ARGB_8888);
new Canvas(argbBitmap).drawBitmap(maybeAlphaSafe, 0 /*left*/, 0 /*top*/, null /*pain*/);
// We now own this Bitmap. It's our responsibility to replace it in the pool outside this method
// when we're finished with it.
return argbBitmap;
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
messageDigest.update(ID_BYTES);
byte[] radiusData = ByteBuffer.allocate(4).putInt(mRadius).array();
messageDigest.update(radiusData);
}
}
使用
RequestOptions requestOptions1 = new RequestOptions();
requestOptions1.transform(new MyBlurTransformation(this));
GlideApp.with(this)
.load(url)
.apply(requestOptions1)
.into(imageView7);
目标(Target)
- BaseTarget<Z> (implements Target<R>)
- SimpleTarget<Z>
- AppWidgetTarget
- NotificationTarget
- PreloadTarget<Z>
- ViewTarget<T,Z>
- ImageViewTarget<Z>
- BitmapImageViewTarget
- DrawableImageViewTarget
- GlideDrawableImageViewTarget
- Drawable
- GlideDrawable (implements Animatable)
- SquaringDrawable
- SimpleTarget<Z>
- ImageViewTargetFactory
这里加载到的目标是simpleTarget,等加载完会返回resource像素图。
/**
* 不显示到ImageView里
*/
SimpleTarget<Bitmap> simpleTarget = new SimpleTarget<Bitmap>(50,50){
@Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
imageView4.setImageBitmap(resource);
}
};
GlideApp.with(this).asBitmap().load(url)
.into(simpleTarget);
过渡(Transition)
有三种类型
- GenericTransitionOptions:通过类型
- DrawableTransitionOptions:要求Drawable
- BitmapTransitionOptions:要求Bitmap
GlideApp.with(this).asBitmap().load(url)
.transition(BitmapTransitionOptions.withCrossFade())
.into(imageView6);
通过TransitionOptions用动画资源文件自定义动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="10000">
<alpha
android:fromAlpha="0.5"
android:toAlpha="1.0" />
<scale
android:fromXScale="0.5"
android:fromYScale="0.5"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.0"
android:toYScale="1.0" />
</set>
BitmapTransitionOptions bitmapTransitionOptions = new BitmapTransitionOptions();
bitmapTransitionOptions.transition(R.anim.glide_animate);
GlideApp.with(this).asBitmap().load(url)
.transition(bitmapTransitionOptions)
.into(imageView6);
通过TransitionOptions用ViewPropertyTransition.Animator自定义动画
public class MyAnimator implements ViewPropertyTransition.Animator {
@Override
public void animate(View view) {
final View finalView = view;
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.setDuration(10000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
finalView.setScaleX((float) (0.5 + 0.5 * value));
finalView.setScaleY((float) (0.5 + 0.5 * value));
finalView.setRotation(180* value);
finalView.setAlpha(value);
}
});
valueAnimator.start();
}
}
BitmapTransitionOptions bitmapTransitionOptions = new BitmapTransitionOptions();
bitmapTransitionOptions.transition(new MyAnimator());
GlideApp.with(this).asBitmap().load(url)
.transition(bitmapTransitionOptions)
.into(imageView6);
自定义GlideModule
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {
//注册自定义组件
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
}
//全局配置Glide
@Override
public void applyOptions(Context context, GlideBuilder builder) {
}
// 避免二次加载
@Override
public boolean isManifestParsingEnabled() {
return false;
}
}
全局配置
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// Default empty impl.
//设置Bitmap的缓存池
builder.setBitmapPool(new LruBitmapPool(30));
//设置内存缓存
builder.setMemoryCache(new LruResourceCache(30));
//设置磁盘缓存
builder.setDiskCache(new InternalCacheDiskCacheFactory(context));
//设置读取不在缓存中资源的线程
builder.setResizeExecutor(GlideExecutor.newSourceExecutor());
//设置读取磁盘缓存中资源的线程
builder.setDiskCacheExecutor(GlideExecutor.newDiskCacheExecutor());
//设置日志级别
builder.setLogLevel(Log.VERBOSE);
//设置全局选项
RequestOptions requestOptions = new RequestOptions();
requestOptions.format(DecodeFormat.PREFER_RGB_565);
builder.setDefaultRequestOptions(requestOptions);
}
自定义组件
Photo.class
public class Photo {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
PhotoModelLoader.class
public class PhotoModelLoader extends BaseGlideUrlLoader<Photo> {
public static class Factory implements ModelLoaderFactory<Photo, InputStream> {
private final ModelCache<Photo, GlideUrl> modelCache = new ModelCache<Photo, GlideUrl>(500);
@Override
public ModelLoader<Photo, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new PhotoModelLoader(multiFactory.build(GlideUrl.class, InputStream.class),
modelCache);
}
@Override
public void teardown() {
}
}
protected PhotoModelLoader(ModelLoader<GlideUrl, InputStream> concreteLoader) {
super(concreteLoader);
}
protected PhotoModelLoader(ModelLoader<GlideUrl, InputStream> concreteLoader, @Nullable ModelCache<Photo, GlideUrl> modelCache) {
super(concreteLoader, modelCache);
}
@Override
protected String getUrl(Photo photo, int width, int height, Options options) {
return photo.getUrl();
}
@Override
public boolean handles(Photo photo) {
return true;
}
}
使用时,注册组件和模块加载器
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
registry.append(Photo.class, InputStream.class,new PhotoModelLoader.Factory());
}
其他
设置优先级
GlideApp.with(this).load(url).priority(Priority.HIGH)
设置缩略图
GlideApp.with(this).load(url).thumbnail(0.3f);
清除内存缓存(需要在UI线程里调用)
GlideApp.get(this).clearMemory();
清除磁盘缓存(需要在子线程里调用)
GlideApp.get(this).clearDiskCache();
Recycle的加载优化
只在拖动和静止时加载,自动滑动时不加载
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case RecyclerView.SCROLL_STATE_DRAGGING:
Glide.with(MainActivity.this).resumeRequests();//加载
break;
case RecyclerView.SCROLL_STATE_SETTLING:
Glide.with(MainActivity.this).pauseRequests();//暂停加载
break;
case RecyclerView.SCROLL_STATE_IDLE:
Glide.with(MainActivity.this).resumeRequests();//加载
break;
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
参考
http://www.jianshu.com/p/5d156bdee68b
https://futurestud.io/tutorials/glide-getting-started
网友评论
在onCreate里调用的时候你跟踪一下RequestTracker.pauseRequests()方法,因为request.isRunning都是false的不会进pause会无效
https://github.com/iamludaxu/ae/blob/master/app/src/main/java/gift/witch/android/ae/glide/GlideActivity.java
https://github.com/iamludaxu/ae/blob/master/glide/src/main/java/gift/witch/glide/OkHttpsUrlLoader.java
https://github.com/iamludaxu/ae/blob/master/glide/src/main/java/gift/witch/glide/OkHttpStreamFetcher.java
https://github.com/iamludaxu/ae/blob/master/glide/src/main/java/gift/witch/glide/MyAppGlideModule.java