Glide源码学习七:自定义模块功能

}

通过这样配置之后,使用Glide加载的所有图片都将会使用ARGB_8888的格式,虽然图片质量变好了,但同时内存开销也会明显增大,所以你要做好心理准备哦。

好了,关于更改Glide配置的内容就介绍这么多,接下来就让我们进入到下一个非常重要的主题,替换Glide组件。

替换Glide组件

=========

替换Glide组件功能需要在自定义模块的registerComponents()方法中加入具体的替换逻辑。相比于更改Glide配置,替换Glide组件这个功能的难度就明显大了不少。Glide中的组件非常繁多,也非常复杂,但其实大多数情况下并不需要我们去做什么替换。不过,有一个组件却有着比较大的替换需求,那就是Glide的HTTP通讯组件。

默认情况下,Glide使用的是基于原生HttpURLConnection进行订制的HTTP通讯组件,但是现在大多数的Android开发者都更喜欢使用OkHttp,因此将Glide中的HTTP通讯组件修改成OkHttp的这个需求比较常见,那么今天我们也会以这个功能来作为例子进行讲解。

首先来看一下Glide中目前有哪些组件吧,在Glide类的构造方法当中,如下所示:

public class Glide {

Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {

register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());

register(File.class, InputStream.class, new StreamFileLoader.Factory());

register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());

register(int.class, InputStream.class, new StreamResourceLoader.Factory());

register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());

register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());

register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());

register(String.class, InputStream.class, new StreamStringLoader.Factory());

register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());

register(Uri.class, InputStream.class, new StreamUriLoader.Factory());

register(URL.class, InputStream.class, new StreamUrlLoader.Factory());

register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());

register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());

}

}

可以看到,这里都是以调用register()方法的方式来注册一个组件,register()方法中传入的参数表示Glide支持使用哪种参数类型来加载图片,以及如何去处理这种类型的图片加载。举个例子:

register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());

这句代码就表示,我们可以使用Glide.with(context).load(new GlideUrl(“url…”)).into(imageView)的方式来加载图片,而HttpUrlGlideUrlLoader.Factory则是要负责处理具体的网络通讯逻辑。如果我们想要将Glide的HTTP通讯组件替换成OkHttp的话,那么只需要在自定义模块当中重新注册一个GlideUrl类型的组件就行了。

说到这里有的朋友可能会疑问了,我们平时使用Glide加载图片时,大多数情况下都是直接将图片的URL字符串传入到load()方法当中的,很少会将它封装成GlideUrl对象之后再传入到load()方法当中,那为什么只需要重新注册一个GlideUrl类型的组件,而不需要去重新注册一个String类型的组件呢?其实道理很简单,因为load(String)方法只是Glide给我们提供一种简易的API封装而已,它的底层仍然还是调用的GlideUrl组件,因此我们在替换组件的时候只需要直接替换最底层的,这样就一步到位了。

那么接下来我们就开始学习到底如何将Glide的HTTP通讯组件替换成OkHttp。

首先第一步,不用多说,肯定是要先将OkHttp的库引入到当前项目中,如下所示:

dependencies {

compile ‘com.squareup.okhttp3:okhttp:3.9.0’

}

然后接下来该怎么做呢?我们只要依葫芦画瓢就可以了。刚才不是说Glide的网络通讯逻辑是由HttpUrlGlideUrlLoader.Factory来负责的吗,那么我们就来看一下它的源码:

public class HttpUrlGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {

private final ModelCache<GlideUrl, GlideUrl> modelCache;

public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {

private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<GlideUrl, GlideUrl>(500);

@Override

public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {

return new HttpUrlGlideUrlLoader(modelCache);

}

@Override

public void teardown() {

}

}

public HttpUrlGlideUrlLoader() {

this(null);

}

public HttpUrlGlideUrlLoader(ModelCache<GlideUrl, GlideUrl> modelCache) {

this.modelCache = modelCache;

}

@Override

public DataFetcher getResourceFetcher(GlideUrl model, int width, int height) {

GlideUrl url = model;

if (modelCache != null) {

url = modelCache.get(model, 0, 0);

if (url == null) {

modelCache.put(model, 0, 0, model);

url = model;

}

}

return new HttpUrlFetcher(url);

}

}

可以看到,HttpUrlGlideUrlLoader.Factory是一个内部类,外层的HttpUrlGlideUrlLoader类实现了ModelLoader<GlideUrl, InputStream>这个接口,并重写了getResourceFetcher()方法。而在getResourceFetcher()方法中,又创建了一个HttpUrlFetcher的实例,在这里才是真正处理具体网络通讯逻辑的地方,代码如下所示:

public class HttpUrlFetcher implements DataFetcher {

private static final String TAG = “HttpUrlFetcher”;

private static final int MAXIMUM_REDIRECTS = 5;

private static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY = new DefaultHttpUrlConnectionFactory();

private final GlideUrl glideUrl;

private final HttpUrlConnectionFactory connectionFactory;

private HttpURLConnection urlConnection;

private InputStream stream;

private volatile boolean isCancelled;

public HttpUrlFetcher(GlideUrl glideUrl) {

this(glideUrl, DEFAULT_CONNECTION_FACTORY);

}

HttpUrlFetcher(GlideUrl glideUrl, HttpUrlConnectionFactory connectionFactory) {

this.glideUrl = glideUrl;

this.connectionFactory = connectionFactory;

}

@Override

public InputStream loadData(Priority priority) throws Exception {

return loadDataWithRedirects(glideUrl.toURL(), 0 , null , glideUrl.getHeaders());

}

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)

throws IOException {

if (redirects >= MAXIMUM_REDIRECTS) {

throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + “) redirects!”);

} else {

try {

if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {

throw new IOException(“In re-direct loop”);

}

} catch (URISyntaxException e) {

}

}

urlConnection = connectionFactory.build(url);

for (Map.Entry<String, String> headerEntry : headers.entrySet()) {

urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());

}

urlConnection.setConnectTimeout(2500);

urlConnection.setReadTimeout(2500);

urlConnection.setUseCaches(false);

urlConnection.connect();

if (isCancelled) {

return null;

}

final int statusCode = urlConnection.getResponseCode();

if (statusCode / 100 == 2) {

return getStreamForSuccessfulRequest(urlConnection);

} else if (statusCode / 100 == 3) {

String redirectUrlString = urlConnection.getHeaderField(“Location”);

if (TextUtils.isEmpty(redirectUrlString)) {

throw new IOException(“Received empty or null redirect url”);

}

URL redirectUrl = new URL(url, redirectUrlString);

return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);

} else {

if (statusCode == -1) {

throw new IOException(“Unable to retrieve response code from HttpUrlConnection.”);

}

throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());

}

}

private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)

throws IOException {

if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {

int contentLength = urlConnection.getContentLength();

stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);

} else {

stream = urlConnection.getInputStream();

}

return stream;

}

@Override

public void cleanup() {

if (stream != null) {

try {

stream.close();

} catch (IOException e) {

}

}

if (urlConnection != null) {

urlConnection.disconnect();

}

}

@Override

public String getId() {

return glideUrl.getCacheKey();

}

@Override

public void cancel() {

isCancelled = true;

}

interface HttpUrlConnectionFactory {

HttpURLConnection build(URL url) throws IOException;

}

private static class DefaultHttpUrlConnectionFactory implements HttpUrlConnectionFactory {

@Override

public HttpURLConnection build(URL url) throws IOException {

return (HttpURLConnection) url.openConnection();

}

}

}

上面这段代码看上去应该不费力吧?其实就是一些HttpURLConnection的用法而已。那么我们只需要仿照着HttpUrlFetcher的代码来写,并且把HTTP的通讯组件替换成OkHttp就可以了。

现在新建一个OkHttpFetcher类,并且同样实现DataFetcher接口,代码如下所示:

public class OkHttpFetcher implements DataFetcher {

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;

}

}

上面这段代码完全就是我照着HttpUrlFetcher依葫芦画瓢写出来的,用的也都是一些OkHttp的基本用法,相信不需要再做什么解释了吧。可以看到,使用OkHttp来编写网络通讯的代码要比使用HttpURLConnection简单很多,代码行数也少了很多。注意在第22行,我添加了一个httplib: OkHttp的请求头,这个是待会儿我们用来进行测试验证的,大家实际项目中的代码无须添加这个请求头。

那么我们就继续发挥依葫芦画瓢的精神,仿照着HttpUrlGlideUrlLoader再写一个OkHttpGlideUrlLoader吧。新建一个OkHttpGlideUrlLoader类,并且实现ModelLoader<GlideUrl, InputStream>接口,代码如下所示:

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 getResourceFetcher(GlideUrl model, int width, int height) {

return new OkHttpFetcher(okHttpClient, model);

}

}

注意这里的Factory我提供了两个构造方法,一个是不带任何参数的,一个是带OkHttpClient参数的。如果对OkHttp不需要进行任何自定义的配置,那么就调用无参的Factory构造函数即可,这样会在内部自动创建一个OkHttpClient实例。但如果你需要想添加拦截器,或者修改OkHttp的默认超时等等配置,那么就自己创建一个OkHttpClient的实例,然后传入到Factory的构造方法当中就行了。

好了,现在就只差最后一步,将我们刚刚创建的OkHttpGlideUrlLoader和OkHttpFetcher注册到Glide当中,将原来的HTTP通讯组件给替换掉,如下所示:

public class MyGlideModule implements GlideModule {

@Override

public void registerComponents(Context context, Glide glide) {

glide.register(GlideUrl.class, InputStream.class, new OkHttpGlideUrlLoader.Factory());

}

}

可以看到,这里也是调用了Glide的register()方法来注册组件的。register()方法中使用的Map类型来存储已注册的组件,因此我们这里重新注册了一遍GlideUrl.class类型的组件,就把原来的组件给替换掉了。

理论上来说,现在我们已经成功将Glide的HTTP通讯组件替换成OkHttp了,现在唯一的问题就是我们该如何去验证一下到底有没有替换成功呢?

验证的方式我倒是想了很多种,比如添加OkHttp拦截器,或者自己架设一个测试用的服务器都是可以的。不过为了让大家最直接地看到验证结果,这里我准备使用Fiddler这个抓包工具来进行验证。这个工具的用法非常简单,但是限于篇幅我就不在本篇文章中介绍这个工具的用法了,还没用过这个工具的朋友们可以通过 这篇文章 了解一下。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

结尾

最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。

首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)

泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter

接下来是资料清单:(敲黑板!!!

领取通道在这里给你们摆上了~

点击我的GitHub免费获取

1.数据结构和算法

2.设计模式

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。


丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)

代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter

[外链图片转存中…(img-u4gYqwWc-1710661869932)]

接下来是资料清单:(敲黑板!!!

领取通道在这里给你们摆上了~

点击我的GitHub免费获取

1.数据结构和算法

[外链图片转存中…(img-JR8BksfQ-1710661869932)]

2.设计模式

[外链图片转存中…(img-JRtH98Of-1710661869933)]

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

[外链图片转存中…(img-nLNxRmkd-1710661869933)]

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

[外链图片转存中…(img-X5XRO9S3-1710661869933)]

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

[外链图片转存中…(img-tuYkcYvL-1710661869934)]

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值