Glide源码解析(四):自定义模块

Glide配置和自定义组件流程

先看下GlideModule接口

1
2
3
4
public interface GlideModule {
void applyOptions(Context context, GlideBuilder builder);
void registerComponents(Context context, Glide glide);
}

实现GlideModule接口

1
2
3
4
5
6
7
8
9
public class CustomGlideMoudle implements GlideModule{
private static String TAG = "CustomGlideMoudle";
@Override
public void applyOptions(Context context, GlideBuilder builder) {
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}

在AndroidManifest.xml中进行配置

1
2
3
<meta-data
android:name="xxx.xxx.xxx.glide.configure.CustomGlideMoudle"
android:value="GlideModule" />

Glide配置项解析

Glide的配置即在applyOptions()中的GlideBuilder一共有下面这些项可以配置。

1
2
3
4
@Override
public void applyOptions(Context context, GlideBuilder builder) {
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}

  • setDecodeFormat(DecodeFormat.PREFER_ARGB_8888):将Glide的解码格式设置为ARGB_8888的,默认是RGB_565。
  • setBitmapPool(new BitmapPool(){}):BitmapPool是bitmap缓存池的实现的接口,自定义配置的话,实现这个接口里边的方法就可以了。BitmapPool的默认实现是在sdk api11之前是空实现,在api11之后是LruBitmapPool这个类实现的,有对自定义实现bitmap缓存池感兴趣的可以用LruBitmapPool作为参考研究。
  • setDiskCache(new DiskCache.Factory(){}):设置磁盘缓存,默认的磁盘路径和大小是image_manager_disk_cache 和 250M,可以重新实现这个接口,修改目录和大小。
  • setDiskCacheService(ExecutorService):设置磁盘缓存线程执行器,这个方法可以使用app中共用的线程执行器,每个开源组件都有自己的线程池和执行器,避免app的线程过多问题。
  • setMemoryCache(new MemoryCache(){}):Glide内存缓存资源的缓存实现,可以按照这个接口自己实现,默认实现是使用的LruCache。
  • setResizeService(ExecutorService):设置图片从原始图片的尺寸转换到要放到ImageView中的尺寸大小的线程执行器,也可以使用系统共用的线程执行器。

    Glide自定义组件流程分析

    1
    2
    3
    4
    @Override
    public void registerComponents(Context context, Glide glide) {

    }

先看下这个register()方法

1
2
3
4
5
6
public <T, Y> void register(Class<T> modelClass, Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory) {
ModelLoaderFactory<T, Y> removed = loaderFactory.register(modelClass, resourceClass, factory);
if (removed != null) {
removed.teardown();
}
}
  • 第一个参数modleClass是请求参数类型
  • 第二个参数resourceClass是将请求参数转换的类型
  • 第三个参数factory就是将第一个参数转换成第二个参数的方法的类型。

ModelLoader使用流程

GenericLoaderFactory.buildModelLoader(modelClass, resourceClass)
这个方法返回值是ModelLoader。

1
2
3
public interface ModelLoader<T, Y> {
DataFetcher<Y> getResourceFetcher(T model, int width, int height);
}

调用ModelLoader的getResourceFetcher()方法会返回DataFetcher的对象
DataFetcher中的loadData()方法就是执行转换数据的过程。这种以factory存储类型转换处理的方式可以在自己的app开发中学习使用。
Glide中一共有13个register()方法,只有下面这两个是直接实现的,其他的都是在这两个基础上调用这两个实现的。

1
2
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());

例如:

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

就是调用的factories.buildModelLoader(Uri.class, ParcelFileDescriptor.class)。最终的就是上面那两个实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class FileDescriptorFileLoader extends FileLoader<ParcelFileDescriptor>
implements FileDescriptorModelLoader<File> {
public static class Factory implements ModelLoaderFactory<File, ParcelFileDescriptor> {
@Override
public ModelLoader<File, ParcelFileDescriptor> build(Context context, GenericLoaderFactory factories) {
return new FileDescriptorFileLoader(factories.buildModelLoader(Uri.class, ParcelFileDescriptor.class));
}
@Override
public void teardown() {
// Do nothing.
}
}
public FileDescriptorFileLoader(Context context) {
this(Glide.buildFileDescriptorModelLoader(Uri.class, context));
}
public FileDescriptorFileLoader(ModelLoader<Uri, ParcelFileDescriptor> uriLoader) {
super(uriLoader);
}
}

介绍一下直接实现的register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());

1
2
3
4
5
6
7
8
9
10
11
12
13
public class StreamByteArrayLoader implements StreamModelLoader<byte[]> {
... ...
@Override
public DataFetcher<InputStream> getResourceFetcher(byte[] model, int width, int height) {
return new ByteArrayFetcher(model, id);
}
public static class Factory implements ModelLoaderFactory<byte[], InputStream> {
@Override
public ModelLoader<byte[], InputStream> build(Context context, GenericLoaderFactory factories) {
return new StreamByteArrayLoader();
}
... ...
}

这个类最终调用的new ByteArrayFetcher(model, id);

1
2
3
4
5
6
7
8
public class ByteArrayFetcher implements DataFetcher<InputStream> {
... ...
@Override
public InputStream loadData(Priority priority) {
return new ByteArrayInputStream(bytes);
}
... ...
}

其实就是将字节数组转换成了输入流的方法。
还有另外一个直接实现的方法:

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

下面是转换类的源码HttpUrlGlideUrlLoader.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HttpUrlGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
... ...
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
... ...
@Override
public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
return new HttpUrlGlideUrlLoader(modelCache);
}
}
... ...
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
... ...
return new HttpUrlFetcher(url);
}
}

下面是HttpUrlFetcher的实现源码,内容就是用HttpURLConnection作网络请求的一个过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
Map<String, String> headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException("In re-direct loop");

}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}

urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);

// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);

// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}

private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
}
stream = urlConnection.getInputStream();
}
return stream;
}

Glide配置项和自定义组件的加载机制

要想知道Glide配置项和自定义组件是如何加载的,还是要从Glide.with(this).load(url).into(imageView);流程说起。
最开始是Glide.with()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}

return glide;
}
private static void checkAndInitializeGlide(@NonNull Context context) {
if (isInitializing) {
throw new IllegalStateException("You cannot call Glide.get() in registerComponents(),"
+ " use the provided Glide instance instead");
}
isInitializing = true;
initializeGlide(context);
isInitializing = false;
}
private static void initializeGlide(@NonNull Context context) {
initializeGlide(context, new GlideBuilder());
}

private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
manifestModules = new ManifestParser(applicationContext).parse();
}

if (annotationGeneratedModule != null
&& !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
Set<Class<?>> excludedModuleClasses =
annotationGeneratedModule.getExcludedModuleClasses();
Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
while (iterator.hasNext()) {
com.bumptech.glide.module.GlideModule current = iterator.next();
if (!excludedModuleClasses.contains(current.getClass())) {
continue;
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);
}
iterator.remove();
}
}

if (Log.isLoggable(TAG, Log.DEBUG)) {
for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) {
Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass());
}
}

RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory() : null;
builder.setRequestManagerFactory(factory);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
Glide glide = builder.build(applicationContext);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.registerComponents(applicationContext, glide, glide.registry);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}

  1. 从AndroidManifest.xml中读取配置信息
  2. 执行GlideModule.applyOptions()方法的内容
  3. 执行GlideModule.registerComponents()方法。

Glide已有的开源组件

Glide的http组件已有很多开源的,使用的时候只要配置一下就可以了
使用OkHttp3来作为HTTP通讯组件的配置如下:

1
2
3
4
dependencies {
compile 'com.squareup.okhttp3:okhttp:3.9.0'
compile 'com.github.bumptech.glide:okhttp3-integration:1.5.0@aar'
}

使用Volley来作为HTTP通讯组件的配置如下:

1
2
3
4
dependencies {
compile 'com.github.bumptech.glide:volley-integration:1.5.0@aar'
compile 'com.mcxiaoke.volley:library:1.0.19'
}

您的支持是我原创的动力