Glide4.0梳理

作者: 我叫陆大旭 | 来源:发表于2017-08-28 15:54 被阅读875次
    glide

    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
    • 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

    相关文章

      网友评论

      • 路途等待:pauseRequests() 调用了这个方法 依然会加载图片
        我叫陆大旭:你是不是pauseRequests在oncreate里调用的?
        在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
      • JarryLeo:可以介绍下 Glide4.0 加载 https路径图片的处理吗?
        JarryLeo:@我叫陆大旭 谢谢。
        我叫陆大旭:我写了一个实现,你可以看一下,主要改变网络的请求。
        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
        我叫陆大旭:你那边网络请求框架是不是用okhttp?

      本文标题:Glide4.0梳理

      本文链接:https://www.haomeiwen.com/subject/lcfldxtx.html