Android开发Android开发Android Tips

Glide图片原理解析

2018-08-24  本文已影响18人  金馆长说

1.Glide是什么?

Glide是Google在2014的IO大会发布一款图片处理框架,是目前android领域比较成熟的一款,也是Google官方推荐的图片处理框架,主要支持网络图片、二进制流、drawable资源、本地图片显示,还支持本地视频显示。

2.Glide基本功能使用

  1. 在app级别下面配置gradle
dependencies {
    compile 'com.github.bumptech.glide:glide:3.7.0'
}
  1. 使用起来
    通过Glide类进行一个链式调用,下面代码显示了一张网络图片。

DiskCacheStrategy.NONE 什么都不缓存
DiskCacheStrategy.SOURCE 只缓存全尺寸图
DiskCacheStrategy.RESULT 只缓存最终的加载图
DiskCacheStrategy.ALL 缓存所有版本图(默认行为)

Glide 不仅缓存了全尺寸的图,还会根据 ImageView 大小所生成的图也会缓存起来。比如,请求一个 800x600 的图加载到一个 400x300 的 ImageView 中,Glide默认会将这原图还有加载到 ImageView 中的 400x300 的图也会缓存起来。



 String url = "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2912495429,3557331556&fm=27&gp=0.jpg";
        Glide
                .with(this)
                .load(url)
                .crossFade()
                .override(200, 200)
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .error(R.mipmap.active_user_default_icon)
                .placeholder(R.mipmap.ic_album_image_source_pick)
                .into(img);

获取Glide请求完成的Bitmap
通过前面这种方式我们能很轻松的显示一张,图片到ImgeView上。但是我们有时候想获取加载完成后的Bitmap怎么办呢,可以通过SimpleTarget来获取。

//SimpleTarget 获取bitmap,定义siez
    SimpleTarget<Bitmap> mSimpleTarget = new SimpleTarget<Bitmap>(200, 200) {
        @Override
        public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
            img.setImageBitmap(resource);
        }
    };

 //获取Bitmap的加载方法。
        Glide.with(getApplicationContext())
                .load(url)
                .asBitmap()
                .into(mSimpleTarget);

我们定义了一个SimpleTarget类,里面有一个onResourceReady方法的回调,会返回加载完成的Bitmap对象,into传入这个SimpleTarget对象。

自定义控件使用Glide显示Image
有时候我们自定义的控件也需要显示Imaeg,但是这个控件不是Image类或者子类,那么前面这种方法就会不使用了,我们可以通过ViewTarget来实现这个逻辑。

public class MyImageView extends FrameLayout {
    private ImageView mImageView;

    public MyImageView(@NonNull Context context) {
        super(context);
    }

    public MyImageView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyImageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mImageView = new ImageView(getContext());
        addView(mImageView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    public void setImage(Drawable drawable) {
        mImageView.setImageDrawable(drawable);
    }
}
 MyImageView img3;
 //ViewTarget自定义View也支持获取Bitmap
        ViewTarget viewTarget = new ViewTarget<MyImageView, GlideDrawable>(img3) {
            @Override
            public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
                img3.setImage(resource);
            }
        };

        Glide.with(getApplicationContext())
                .load(url)
                .into(viewTarget);

通过定义一个ViewTarget传入了二个泛型类,一个是MyImageView,一个是GlideDrawable,传入了img3。通过onResourceReady的回调我们可以对自定义控件进行图片显示了。

3.Glide原理了解?

Glide的with、load、into虽然只有三个方法调用但是内部的逻辑是很复杂的,成吨的代码这里我只做一个大概了解。首先with方法会构造一个单例的RequestManager对象。

with方法

 public static RequestManager with(FragmentActivity activity) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }
  public static RequestManagerRetriever get() {
        return INSTANCE;
    }

可以看出来我们一个程序生命周期内只有一个RequestManager对象,工程师在设计的时候很考虑到性能的问题。

load方法

 public DrawableTypeRequest<String> load(String string) {
        return (DrawableTypeRequest<String>) fromString().load(string);
    }

接着通过RequestManage对象的load方法返回一个DrawableTypeRequest类,它是一个DrawableRequestBuilder的一个子类,通过类名可以看出来主要作用是主要是构建请求中的一些参数用的,比如我们之前写的 override、error、placeholder、diskCacheStrategy这些辅助方法的设置。

into方法
最后的一个方法也是最复杂的一个,是真正请求网络请求的地方。

public Target<GlideDrawable> into(ImageView view) {
        return super.into(view);
    }
 public Target<TranscodeType> into(ImageView view) {
        Util.assertMainThread();
       ...
        return into(glide.buildImageViewTarget(view, transcodeClass));
    }
 @SuppressWarnings("unchecked")
    public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
        if (GlideDrawable.class.isAssignableFrom(clazz)) {
            return (Target<Z>) new GlideDrawableImageViewTarget(view);
        } else if (Bitmap.class.equals(clazz)) {
            return (Target<Z>) new BitmapImageViewTarget(view);
        } else if (Drawable.class.isAssignableFrom(clazz)) {
            return (Target<Z>) new DrawableImageViewTarget(view);
        } else {
            throw new IllegalArgumentException("Unhandled class: " + clazz
                    + ", try .as*(Class).transcode(ResourceTranscoder)");
        }
    }

into接收一个ViewTeger,

into的调用


 public <Y extends Target<TranscodeType>> Y into(Y target) {
      ...
        Request request = buildRequest(target);
        target.setRequest(request);
        lifecycle.addListener(target);
        requestTracker.runRequest(request);

        return target;
    }
 public void runRequest(Request request) {
        requests.add(request);
        if (!isPaused) {
            request.begin();
        } else {
            pendingRequests.add(request);
        }
    }

runRequest方法来控制请求的队列,如果当前视图状态是Paused状态就会把request添加到等待请求的队列,如果不是则直接执行。里面会有一个HttpUrlFetcher来执行前面封装好的网络请求

4.Glide是如何缓存的?

glide缓存主要分为

  1. 内存缓存

内存缓存主要是防止同样的图片重复读取到内存中来,解约JVM的内存空间

glide的内存缓存机制是使用LruCache算法实现,首先需要会需要一个key,这个key是通过url+构建请求时候的参数决定的。keyFactory.buildKey的代码就是生成Key的逻辑。glide内存缓存会调用二个方法来获取缓存。

内存缓存的逻辑就是,先会在loadFromCache中去取缓存,这个是一个LruCaChe算啊实现的,如果取到了值就会把它存入一个弱引用中。这样的防止被LruCache算法回收掉,如果LruCache没有取到就会去loadFromActiveResources方法里面取。

如果都没有那就会开启子线程去网络请求图片。

 public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
            DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
            Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
        Util.assertMainThread();
        long startTime = LogTime.getLogTime();

        final String id = fetcher.getId();
        EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                transcoder, loadProvider.getSourceEncoder());

        EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
        if (cached != null) {
            cb.onResourceReady(cached);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from cache", startTime, key);
            }
            return null;
        }

        EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
        if (active != null) {
            cb.onResourceReady(active);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from active resources", startTime, key);
            }
            return null;
        }

        EngineJob current = jobs.get(key);
        if (current != null) {
            current.addCallback(cb);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Added to existing load", startTime, key);
            }
            return new LoadStatus(cb, current);
        }

        EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
        DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
                transcoder, diskCacheProvider, diskCacheStrategy, priority);
        EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
        jobs.put(key, engineJob);
        engineJob.addCallback(cb);
        engineJob.start(runnable);

        if (Log.isLoggable(TG, Log.VERBOSE)) {
            logWithTimeAndKey("Started new load", startTime, key);
        }
        return new LoadStatus(cb, engineJob);
    }
  1. 磁盘缓存

磁盘缓存主要是防止同一张网络图片,重复从网络中读取和下载,磁盘缓存也是使用LruCache算法实现的。

磁盘缓存有一个逻辑,当需要去加载一张图片的时候,Glide默认不会显示原始图片,而是会对图片进行压缩转换。经过这些转换操作之后才会把,图片显示出来,磁盘缓存默认就是缓存转换后的图片。

Glide

Glide的缓存策略是如果加载是一张1000 * 1000大小的图片,但是最终显示到ImageView上的只有500 * 500,Glide只会缓存最终显示的那张500*500的图片。

Glide的四种缓存策略

  1. DiskCacheStrategy.NONE 不缓存文件
  2. DiskCacheStrategy.SOURCE 只缓存原图
  3. DiskCacheStrategy.RESULT 只缓存最终加载的图(默认的缓存策略)
  4. DiskCacheStrategy.ALL 同时缓存原图和结果图

配置缓存策略
通过实现GlideModel接口来定义一个自定义的缓存策略类

 public class DiskCachMoudle implements GlideModule {

        @Override
        public void applyOptions(Context context, GlideBuilder builder) {

        }

        @Override
        public void registerComponents(Context context, Glide glide) {

        }
    }
<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".test.Activity2">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="com.jin.mak.DiskCachMoudle"
            android:value="DiskCachMoudle" />
    </application>

Glide源码里面的get方法会自动读取我们在ManifestParert中配置的那个DiskCachMoudle配置。

public static Glide get(Context context) {
        if (glide == null) {
            synchronized (Glide.class) {
                if (glide == null) {
                    Context applicationContext = context.getApplicationContext();

                    //第一步
                    List<GlideModule> modules = new ManifestParser(applicationContext).parse();
                    
                     //第二步
                    GlideBuilder builder = new GlideBuilder(applicationContext);
                    for (GlideModule module : modules) {
                        //在builder构造出glide之前,读取使用者自定义的配置.
                        module.applyOptions(applicationContext, builder);
                    }
                    glide = builder.createGlide();

                    //第三步
                    for (GlideModule module : modules) {
                        module.registerComponents(applicationContext, glide);
                    }
                }
            }
        }

        return glide;
    }


applyOptions 方法

上一篇 下一篇

猜你喜欢

热点阅读