Glide日常使用以及重难点解读

Posted Jason_Lee155

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Glide日常使用以及重难点解读相关的知识,希望对你有一定的参考价值。

Glide和Picasso相比较:

​1.Glide可以gif动态图,Picasson不可以

2.Glide默认Bitmap格式是RGB_565,图片质量不如Picasso(ARGB_8888)加载的清晰,但耗内存小.(但Glide也可以准换成ARGB_8888,而且耗内存也相对小些) ​

  • 2.1,如果你对默认的RGB_565效果还比较满意,可以不做任何事,但是如果你觉得难以接受,可以创建一个新的GlideModule将Bitmap格式转换到ARGB_8888:    
public class GlideConfiguration implements GlideModule {               
    @Override                 
    public void applyOptions(Context context, GlideBuilder builder) {             
        // Apply options to the builder here.
        builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);         
    }
               
    @Override         
    public void registerComponents(Context context, Glide glide) {             
        // register ModelLoaders here.         
    }     
} 
  • 2.2,同时在androidManifest.xml中将GlideModule定义为meta-data:
<meta-data 
    android:name="com.inthecheesefactory.lab.glidepicasso.GlideConfiguration"
    android:value="GlideModule"/>

3,Picasso的大小大约是118KB,而Glide大约有430KB。

 (Fresco加载大图速度更快,但fresco 最大只支持图片文件大小为 2M 。)

基本使用

添加依赖

//不同版本依赖不同
implementation 'com.github.bumptech.glide:glide:3.7.0'
Glide.with(this)
     .load(url) //图片地址
     .placeholder(R.drawable.loading)  //占位图
     .error(R.drawable.error) //异常图
     .diskCacheStrategy(DiskCacheStrategy.NONE) //禁用缓存
     .override(100, 100) //固定图片的像素
     .asBitmap()   //强制使用图片,不能加载动图
     .asGif()       //强制使用动图,不能加载图片
     .thumbnail(1f) //缩略图
     .crossFade() //淡入淡出效果
     .bitmapTransform(new CropCircleTransformation(this))  //图片转换
     .into(imageView);
  • 图片未加载时的占位图以及图片加载失败的图片展示
Glide.with(this).load(url).placeholder(R.mipmap.place).error(R.mipmap.icon_photo_error).into(iv);
  • Glide默认是包含淡入淡出动画的时间为300ms(毫秒),我们可以修改这个动画的时间,
Glide.with(this).load(url).placeholder(R.mipmap.place).error(R.mipmap.icon_photo_error).crossFade(5000).into(iv);
  • 取消淡入淡出动画
Glide.with(this).load(url).placeholder(R.mipmap.place).error(R.mipmap.icon_photo_error).dontAnimate().into(iv);
  • 加载本地图片
// 判断SD卡是否存在,并且是否具有读写权限
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
    Glide.with(this).load(new File(Environment.getExternalStorageDirectory(), "meinv.png")).into(iv);
  • 加载动态图
Glide.with(this).load(gifUrl).placeholder(R.mipmap.place).error(R.mipmap.icon_photo_error).into(iv);
  • 加载动态图,先判断是否是动态图,不是的话加载错误图片,只需要调用asGif()判断即可
Glide.with(this).load(gifUrl).asGif().placeholder(R.mipmap.place).error(R.mipmap.icon_photo_error).into(iv);
  • 加载动态图的第一帧的图片,asBitmap()
Glide.with(this).load(gifUrl).asBitmap().placeholder(R.mipmap.place).error(R.mipmap.icon_photo_error).into(iv);
  • 加载动态图时,使用diskCacheStrategy(),速度回快些,效率高些,因为这是把gif资源缓存到磁盘
Glide.with(this).load(gifUrl).diskCacheStrategy(DiskCacheStrategy.SOURCE).placeholder(R.mipmap.place).error(R.mipmap.icon_photo_error).into(iv);
  • 内存不缓存,磁盘缓存缓存所有图片
Glide.with(this).load(mUrl).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.ALL).into(iv);

设置内存    skipMemoryCache(true).

设置磁盘    diskCacheStrategy (DiskCacheStrategy.ALL)

 磁盘模式

DiskCacheStrategy.NONE:表示不缓存任何内容。
DiskCacheStrategy.SOURCE:表示只缓存原始图片。
DiskCacheStrategy.RESULT:表示只缓存转换过后的图片(默认选项)。
DiskCacheStrategy.ALL :表示既缓存原始图片,也缓存转换过后的图片。

  • 内存缓存处理图,磁盘缓存原图
Glide.with(this).load(mUrl).skipMemoryCache(false).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(mIv);
  •  加载正方形图形--override(36,36).centerCrop()
Glide.with(this).load(R.drawable.shape_rec).apply(new RequestOptions().override(36,36).centerCrop()).into(iv_head);
  • 加载圆角图片transform()
//(第三方转换框架:https://github.com/wasabeef/glide-transformations)

Glide.with(this).load(url).transform(new CornersTransform()).into(iv1);

public  class CornersTransform extends BitmapTransformation {
    private float radius;

    public CornersTransform(Context context) {
        super(context);
        radius = 10;
    }

    public CornersTransform(Context context, float radius) {
        super(context);
        this.radius = radius;
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return cornersCrop(pool, toTransform);
    }

    private Bitmap cornersCrop(BitmapPool pool, Bitmap source) {
        if (source == null) return null;

        Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        }

        Canvas canvas = new Canvas(result);
        Paint paint  = new Paint();
        paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        paint.setAntiAlias(true);
        RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
        canvas.drawRoundRect(rectF, radius, radius, paint);
        return result;
    }

    @Override
    public String getId() {
        return getClass().getName();
    }
}
  • 预加载图片:如果希望提前对图片进行一个预加载,等真正需要加载图片的时候,直接从缓存中读取,不想再等待慢长的网络加载时间了,就使用预加载.

(如果使用了preload()方法,最好要将diskCacheStrategy的缓存策略指定成DiskCacheStrategy.SOURCE。因为preload()方法默认是预加载的原始图片大小,而into()方法则默认会根据ImageView控件的大小来动态决定加载图片的大小。因此,如果不将diskCacheStrategy的缓存策略指定成DiskCacheStrategy.SOURCE的话,很容易会造成我们在预加载完成之后再使用into()方法加载图片,却仍然还是要从网络上去请求图片这种现象。)

// 实现预加载
Glide.with(this)
     .load(url)
     .diskCacheStrategy(DiskCacheStrategy.SOURCE)
     .preload();
  • downloadOnly(),获取缓存文件的地址
public void downloadImage(View view) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
                final Context context = getApplicationContext();
                FutureTarget<File> target = Glide.with(context).load(url).downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
                final File imageFile = target.get();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, imageFile.getPath(), Toast.LENGTH_LONG).show();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}
  • 滚动加载,不滚动时不加载,提高列表加载数据效率:
Glide.with(context).resumeRequests()

Glide.with(context).pauseRequests()

mRecyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {
 
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    }
 
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
 
        if (mBaseContext == null || mBaseContext.isFinishing()) {
            //避免Glide加载图片抛出异常
            return;
        }
 
        switch (newState) {
            //不滚动,就停止加载
            case RecyclerView.SCROLL_STATE_IDLE:
                Glide.with(mBaseContext).resumeRequests();
                break;
            //滚动,开始加载
            case RecyclerView.SCROLL_STATE_DRAGGING:
            case RecyclerView.SCROLL_STATE_SETTLING:
                Glide.with(mBaseContext).pauseRequests();
                break;
        }
    }
});

自定义

 1. 默认情况下,Glide使用的是基于原生HttpURLConnection进行订制的HTTP通讯组件,但是现在大多数的Android开发者都更喜欢使用OkHttp,因此将Glide中的HTTP通讯组件修改成OkHttp的这个需求如下:

public class OkHttpFetcher implements DataFetcher<InputStream> {
    private final OkHttpClient client;
    private final GlideUrl url;
    private InputStream stream;
    private ResponseBody responseBody;
    private volatile boolean isCancelled;
 
    public OkHttpFetcher(OkHttpClient client, GlideUrl url) {
        this.client = client;
        this.url = url;
    }
 
    @Override
    public InputStream loadData(Priority priority) throws Exception {
        Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl());
        for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
            String key = headerEntry.getKey();
            requestBuilder.addHeader(key, headerEntry.getValue());
        }
        requestBuilder.addHeader("httplib", "OkHttp");
        Request request = requestBuilder.build();
        if (isCancelled) {
            return null;
        }
        Response response = client.newCall(request).execute();
        responseBody = response.body();
        if (!response.isSuccessful() || responseBody == null) {
            throw new IOException("Request failed with code: " + response.code());
        }
        stream = ContentLengthInputStream.obtain(responseBody.byteStream(), responseBody.contentLength());
        return stream;
    }
 
    @Override
    public void cleanup() {
        try {
            if (stream != null) {
                stream.close();
            }
            if (responseBody != null) {
                responseBody.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    @Override
    public String getId() {
        return url.getCacheKey();
    }
 
    @Override
    public void cancel() {
        isCancelled = true;
    }
}
public class OkHttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
    private OkHttpClient okHttpClient;
 
    public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
        private OkHttpClient client;
 
        public Factory() {
        }
 
        public Factory(OkHttpClient client) {
            this.client = client;
        }
 
        private synchronized OkHttpClient getOkHttpClient() {
            if (client == null) {
                client = new OkHttpClient();
            }
            return client;
        }
 
        @Override
        public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new OkHttpGlideUrlLoader(getOkHttpClient());
        }
 
        @Override
        public void teardown() {
        }
    }
 
    public OkHttpGlideUrlLoader(OkHttpClient client) {
        this.okHttpClient = client;
    }
 
    @Override
    public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
        return new OkHttpFetcher(okHttpClient, model);
    }
}
public class GlideConfiguration implements GlideModule {
    public static final int DISK_CACHE_SIZE = 500 * 1024 * 1024;//默认是250M,现在改为500M
 
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);//Glide默认是RGB-565格式,现在改为ARGB-8888
 
        //InternalCacheDiskCacheFactory和ExternalCacheDiskCacheFactory的默认硬盘缓存大小都是250M
        //如果你的应用缓存的图片总大小超出了250M,那么Glide就会按照DiskLruCache算法的原则来清理缓存的图片。
        //builder.setDiskCache(new ExternalCacheDiskCacheFactory(context));//缓存到sd卡上
        builder.setDiskCache(new ExternalCacheDiskCacheFactory(context, DISK_CACHE_SIZE));
    }
 
    @Override
    public void registerComponents(Context context, Glide glide) {
        glide.register(GlideUrl.class, InputStream.class,new OkHttpGlideUrlLoader.Factory());
    }
    
}
// 在清单文件设置meta:
<meta-data android:name="cn.xmqy.hoyouchang.util.GlideConfiguration"
           android:value="GlideModule"/>

封装

public class GlideUtil {
    public static void load(Context context, String url, ImageView imageView, RequestOptions options) {
        Glide.with(context).load(url).apply(options).into(imageView);
    }
}
RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.ic_launcher_background)
        .error(R.drawable.error)
        .diskCacheStrategy(DiskCacheStrategy.NONE);
        .override(200, 100)//指定图片大小
        .skipMemoryCache(true)//取消内存
Glide.with(this).load(url).apply(options).into(imageView);

源码解析

1. Glide采用的是三级缓存,内存–>磁盘–>网络

Glide的缓存功能,大部分都是在load()方法中进行的

缓存的作用:

  •  内存缓存的主要作用 :    是防止应用重复将图片数据读取到内存当中,
  •  磁盘缓存的主要作用 :   是防止应用重复从网络或其他地方重复下载和读取数据。
  •  Glide内存缓存的实现自然也是使用的LruCache算法。并且还结合了一种弱引用的机制,共同完成了内存缓存功能

内存缓存最大空间(maxSize)=每个进程可用的最大内存 * 0.4(低配手机的话是: 每个进程可用的最大内存 * 0.33)

//代码在MemorySizeCalculator中
final int maxSize = getMaxSize(activityManager);

private static int getMaxSize(ActivityManager activityManager) {
    //每个进程可用的最大内存
    final int memoryClassBytes = activityManager.getMemoryClass() * 1024 * 1024;

    //判断是否低配手机
    final boolean isLowMemoryDevice = isLowMemoryDevice(activityManager);

    return Math.round(memoryClassBytes
            * (isLowMemoryDevice ? LOW_MEMORY_MAX_SIZE_MULTIPLIER : MAX_SIZE_MULTIPLIER));
}

磁盘缓存的大小是250M。

2. Glide的对象,是通过调用get()获取,采用的是单例双重检查锁,保证了Glide对象的唯一性:

private static volatile Glide glide;
public static Glide get(Context context) {
 
    if (glide == null) {
 
        //同步Glide
        synchronized (Glide.class) {
            if (glide == null) {
                Context applicationContext = context.getApplicationContext();
 
                //解析清单文件配置的自定义GlideModule的metadata标签,返回一个GlideModule集合
         List<GlideModule> modules = new ManifestParser(applicationContext).parse();
 
                GlideBuilder builder = new GlideBuilder(applicationContext);
 
                //循环集合,执行GlideModule 实现类中的方法
         for (GlideModule module : modules) {
                    module.applyOptions(applicationContext, builder);
                }
                glide = builder.createGlide();
                for (GlideModule module : modules) {
                    //注册组件
 
           module.registerComponents(applicationContext, glide);
                }
            }
        }
    }
 
    return glide;
}

3. Glide.with()

With方法有5个重载的构造方法,运行你在activity,frgament或者其他地方使用。得到一个RequestManager对象,RequestManager实现了LifeCycleListener接口,绑定Activity/Fragment生命周期,对请求进行暂停,恢复,清除操作。

下面是5个构造方法:

//RequestManager实现了LifeCycleListener接口
public static RequestManager with(Context context) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(context);
}
 
public static RequestManager with(Activity activity) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(activity);
}
 
public static RequestManager with(FragmentActivity activity) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(activity);
}
 
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(fragment);
}
 
//V4包的fragment
public static RequestManager with(Fragment fragment) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(fragment);
}

RequestManage对象源码:

public class RequestManager implements LifecycleListener {
    private final Context context;
    private final Lifecycle lifecycle;
    private final RequestManagerTreeNode treeNode;
    private final RequestTracker requestTracker;
    private final Glide glide;
 
    public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
        this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
    }
 
    RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
                   RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
        this.context = context.getApplicationContext();
        this.lifecycle = lifecycle;
        this.treeNode = treeNode;
        this.requestTracker = requestTracker;
 
        //通过Glide的静态方法获取实例对象,Glide是通过单利创建的
    this.glide = Glide.get(context);
        this.optionsApplier = new OptionsApplier();

如果是在子线程调用with(),或者上下文传入的是Application的上下文,那生命周期就与Application的生命周期同步:

public class RequestManagerRetriever implements Handler.Callback{
 
    private RequestManager getApplicationManager(Context context) {
        if (applicationManager == null) {
            synchronized (this) {
                if (applicationManager == null) {
                    applicationManager = new RequestManager(context.getApplicationContext(),
                            new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
                }
            }
        }
 
        return applicationManager;
    }

4. load(url)

Glide的缓存功能,大部分都是在load()方法中进行的。

load()也有很多重载的方法,它可以加载网络图片、本地图片、gif(动态图)图、Uri、File:

public DrawableTypeRequest<String> load(String string) {
    return (DrawableTypeRequest<String>) fromString().load(string);
}
 
//加载Uri
public DrawableTypeRequest<Uri> load(Uri uri) {
    return (DrawableTypeRequest<Uri>) fromUri().load(uri);
}
 
//加载File
public DrawableTypeRequest<File> load(File file) {
    return (DrawableTypeRequest<File>) fromFile().load(file);
}
 
 
 
//直接加载图片资源id,   R.mipmap.ic_launcher
public DrawableTypeRequest<Integer> load(Integer resourceId) {
    return (DrawableTypeRequest<Integer>) fromResource().load(resourceId);
}
 
 
 
//加载URL
@Deprecated
public DrawableTypeRequest<URL> load(URL url) {
    return (DrawableTypeRequest<URL>) fromUrl().load(url);
}

5. Glide的into()方法

public Target<TranscodeType> into(ImageView view) {
    Util.assertMainThread();
    if (view == null) {
        throw new IllegalArgumentException("You must pass in a non null View");
    }
 
    if (!isTransformationSet && view.getScaleType() != null) {
        switch (view.getScaleType()) {
            case CENTER_CROP:
                applyCenterCrop();
                break;
            case FIT_CENTER:
            case FIT_START:
            case FIT_END:
                applyFitCenter();
                break;
            //$CASES-OMITTED$
            default:
                // Do nothing.
        }
    }
 
    return into(glide.buildImageViewTarget(view, transcodeClass));
}
//Target我们可以理解成View,只是Glide对我们的View做了一层封装。
public <Y extends Target<TranscodeType>> Y into(Y target) {
    //判断是否在主线程,(UI界面更新只能在主线程),不在主线程就报异常
    Util.assertMainThread();
    if (target == null) {
        throw new IllegalArgumentException("You must pass in a non null Target");
    }
    if (!isModelSet) {
        throw new IllegalArgumentException("You must first set a model (try #load())");
    }
    //获取request对象
    Request previous = target.getRequest();
    //requestTracker是请求跟踪类对象,主要管理请求的发起,暂停,清除
    if (previous != null) {
        previous.clear();
        requestTracker.removeRequest(previous);
        previous.recycle();
    }
    //创建request对象
    Request request = buildRequest(target);
    target.setRequest(request);
 
    //将target加入lifecycle,绑定生命周期
    lifecycle.addListener(target);
 
    //执行请求
    requestTracker.runRequest(request);
 
    return target;
}

参考

郭霖的专栏

面试题之---Glide源码解析

以上是关于Glide日常使用以及重难点解读的主要内容,如果未能解决你的问题,请参考以下文章

安卓 日常问题 工作日志7

工欲善其事必先利其器之Glide解读

城商行生产环境虚拟化资源池架构设计及应用迁移十个难点解读

解读证券行业生产环境容器云平台规划架构设计四大难点

图片加载Glide的使用以及简单封装

金融行业云管平台架构设计常见难点解读