Glide在使用的时候都是Glide.with(this).load(url).into(imageView);但是Glide如何对参数设置呢?有哪些参数值Glide直接支持的设置呢?本文将对此分析介绍。
目录:
- 1.Glide配置和自定义组件流程
- 2.Glide配置项解析
- 3.Glide自定义组件流程分析
- 4.Glide配置项和自定义组件的加载机制
- 5.Glide已有的开源组件
1、Glide配置和自定义组件流程
Glide中有这样一个接口,可以在app中自定义实现
public interface GlideModule {
void applyOptions(Context context, GlideBuilder builder);
void registerComponents(Context context, Glide glide);
}
Glide要配置和自定义模块只需要两步
1.实现GlideModule接口
public class CustomGlideMoudle implements GlideModule{
private static String TAG = "CustomGlideMoudle";
/**
* 更改Glide的配置
*/
@Override
public void applyOptions(Context context, GlideBuilder builder) {
}
/**
* 替换Glide的组件
*/
@Override
public void registerComponents(Context context, Glide glide) {
}
}
2.在AndroidManifest.xml中进行配置
在
2.Glide配置项解析
Glide的配置即在applyOptions()中的GlideBuilder一共有下面这些项可以配置,我这里的源码是Glide3.6.0.
具体代码是这样:
@Override
public void applyOptions(Context context, GlideBuilder builder) {
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}
1.builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
代码的意思是将Glide的解码格式设置为ARGB_8888的,Glide的默认设置是RGB_565。
2.builder.setBitmapPool(new BitmapPool(){});
BitmapPool是bitmap缓存池的实现的接口,自定义配置的话,实现这个接口里边的方法就可以了。
BitmapPool的默认实现是在sdk api11之前是空实现,在api11之后是LruBitmapPool这个类实现的,有对自定义实现bitmap缓存池感兴趣的可以用LruBitmapPool作为参考研究。
3. builder.setDiskCache(new DiskCache.Factory(){});
设置磁盘缓存,默认的磁盘路径和大小是image_manager_disk_cache 和 250M,可以重新实现这个接口,修改目录和大小。
4.builder.setDiskCacheService(ExecutorService);
设置磁盘缓存线程执行器,这个方法可以使用app中共用的线程执行器,每个开源组件都有自己的线程池和执行器,避免app的线程过多问题
5.builder.setMemoryCache(new MemoryCache(){});
Glide内存缓存资源的缓存实现,可以按照这个接口自己实现,默认实现是使用的LruCache。
6.builder.setResizeService(ExecutorService);
设置图片从原始图片的尺寸转换到要放到ImageView中的尺寸大小的线程执行器,也可以使用系统共用的线程执行器。
Glide支持的全局配置都在这里了。以上这些配置如果自定义配置了,在Glide实例创建的时候就会优先使用自定义配置,如果没有配置会使用默认的配置项。
3.Glide自定义组件流程分析
@Override
public void registerComponents(Context context, Glide glide) {
}
Glide可以注册的组件
先看下这个register()方法
public void register(Class modelClass, Class resourceClass, ModelLoaderFactory factory) {
ModelLoaderFactory removed = loaderFactory.register(modelClass, resourceClass, factory);
if (removed != null) {
removed.teardown();
}
}
GenericLoaderFactory类下面有两个map
- Map
> modelClassToResourceFactories 这个map称为map1 - map1的key是modelClass(register的第一个参数),map1的value是另外一个map(称为map2)**
- map2的key是resourceClass(register的第二个参数),value是factory(register的第三个参数)**
这个register的意思是:
- 第一个参数modleClass是请求参数类型
- 第二个参数resourceClass是将请求参数转换的类型
- 第三个参数factory就是将第一个参数转换成第二个参数的方法的类型。
用第二个map的原因是第一个map中的key的类型有可能是相同的,但是返回值是不同的,所以需要不同的转换器(转换器是指的factory,register的第三个参数)
将这些类型转换的方法放到GenericLoaderFactory.modelClassToResourceFactories 这个map中存储。引擎在需要类型转换的时候回调用这里的factor的方法找对应类型的MolderLoader去做转换。
ModelLoader使用流程
GenericLoaderFactory.buildModelLoader(modelClass, resourceClass)
这个方法返回值是ModelLoader。
public interface ModelLoader {
DataFetcher getResourceFetcher(T model, int width, int height);
}
调用ModelLoader的getResourceFetcher()方法会返回DataFetcher的对象
DataFetcher中的loadData()方法就是执行转换数据的过程。这种以factory存储类型转换处理的方式可以在自己的app开发中学习使用。
Glide中一共有13个register()方法,只有下面这两个是直接实现的,其他的都是在这两个基础上调用这两个实现的。
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());
例如:
register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
就是调用的factories.buildModelLoader(Uri.class, ParcelFileDescriptor.class)。最终的就是上面那两个实现的。
public class FileDescriptorFileLoader extends FileLoader
implements FileDescriptorModelLoader {
public static class Factory implements ModelLoaderFactory {
@Override
public ModelLoader 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 uriLoader) {
super(uriLoader);
}
}
介绍一下直接实现的register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());
public class StreamByteArrayLoader implements StreamModelLoader {
... ...
@Override
public DataFetcher getResourceFetcher(byte[] model, int width, int height) {
return new ByteArrayFetcher(model, id);
}
public static class Factory implements ModelLoaderFactory {
@Override
public ModelLoader build(Context context, GenericLoaderFactory factories) {
return new StreamByteArrayLoader();
}
... ...
}
这个类最终调用的new ByteArrayFetcher(model, id);
public class ByteArrayFetcher implements DataFetcher {
... ...
@Override
public InputStream loadData(Priority priority) {
return new ByteArrayInputStream(bytes);
}
... ...
}
其实就是将字节数组转换成了输入流的方法。
还有另外一个直接实现的方法:
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
下面是转换类的源码HttpUrlGlideUrlLoader.java
public class HttpUrlGlideUrlLoader implements ModelLoader {
... ...
public static class Factory implements ModelLoaderFactory {
... ...
@Override
public ModelLoader build(Context context, GenericLoaderFactory factories) {
return new HttpUrlGlideUrlLoader(modelCache);
}
}
... ...
@Override
public DataFetcher getResourceFetcher(GlideUrl model, int width, int height) {
... ...
return new HttpUrlFetcher(url);
}
}
下面是HttpUrlFetcher的实现源码,内容就是用HttpURLConnection作网络请求的一个过程。
public class HttpUrlFetcher implements DataFetcher {
private static final String TAG = "HttpUrlFetcher";
private static final String ENCODING_HEADER = "Accept-Encoding"; private static final String DEFAULT_ENCODING = "identity";
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); }
// Visible for testing. HttpUrlFetcher(GlideUrl glideUrl, HttpUrlConnectionFactory connectionFactory) {
this.glideUrl = glideUrl; this.connectionFactory = connectionFactory; }
@Override public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders()); }
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map headers)
throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new IOException("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 IOException("In re-direct loop"); }
} catch (URISyntaxException e) {
// Do nothing, this is best effort. }
}
urlConnection = connectionFactory.build(url); for (Map.Entry headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue()); }
// Do our best to avoid gzip since it's both inefficient for images and also makes it more // difficult for us to detect and prevent partial content rendering. See #440. if (TextUtils.isEmpty(urlConnection.getRequestProperty(ENCODING_HEADER))) {
urlConnection.setRequestProperty(ENCODING_HEADER, DEFAULT_ENCODING); }
urlConnection.setConnectTimeout(2500); urlConnection.setReadTimeout(2500); urlConnection.setUseCaches(false); urlConnection.setDoInput(true); // Connect explicitly to avoid errors in decoders if connection fails. 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 {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding()); }
stream = urlConnection.getInputStream(); }
return stream; }
@Override public void cleanup() {
if (stream != null) {
try {
stream.close(); } catch (IOException e) {
// Ignore }
}
if (urlConnection != null) {
urlConnection.disconnect(); }
}
@Override public String getId() {
return glideUrl.getCacheKey(); }
@Override public void cancel() {
// TODO: we should consider disconnecting the url connection here, but we can't do so directly because cancel is // often called on the main thread. 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(); }
}
}
4.Glide配置项和自定义组件的加载机制
要想知道Glide配置项和自定义组件是如何加载的,还是要从Glide.with(this).load(url).into(imageView);流程说起。
最开始是Glide.with()
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity); }
获得了RequestManager
在RequestManager的构造方法中有Glide.get(context);
RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
... ...
this.glide = Glide.get(context);
... ...
}
然后进入get方法中
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext(); List modules = new ManifestParser(applicationContext).parse(); GlideBuilder builder = new GlideBuilder(applicationContext); for (GlideModule module : modules) {
module.applyOptions(applicationContext, builder); }
glide = builder.createGlide(); for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide); }
}
}
}
return glide;
}
可以看到
1.从AndroidManifest.xml中读取配置信息
2.执行GlideModule.applyOptions()方法的内容
3.glide = builder.createGlide();//下边补上源码
4.执行GlideModule.registerComponents()方法。
3步骤的代码,就是GlideModule.applyOptions()不配置就使用默认的,配置了就使用配置的。
Glide createGlide() {
if (sourceService == null) {
final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
sourceService = new FifoPriorityThreadPoolExecutor(cores);
}
if (diskCacheService == null) {
diskCacheService = new FifoPriorityThreadPoolExecutor(1);
}
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
if (bitmapPool == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
int size = calculator.getBitmapPoolSize();
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
}
if (decodeFormat == null) {
decodeFormat = DecodeFormat.DEFAULT;
}
return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}
这里边的return new Glide()调用了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());
... ...
}
这些需要注册的转换器在Glide构造方法中先注册了一下,之后再执行GlideModule.registerComponents()。根据map的机制,会把之前注册过的替换掉。具体源码在GenericLoaderFactory.register()这里就不贴出来了。
5.Glide已有的开源组件
Glide的http组件已有很多开源的,使用的时候只要配置一下就可以了
使用OkHttp3来作为HTTP通讯组件的配置如下:
dependencies {
compile 'com.squareup.okhttp3:okhttp:3.9.0'
compile 'com.github.bumptech.glide:okhttp3-integration:1.5.0@aar'
}
使用OkHttp2来作为HTTP通讯组件的配置如下:
dependencies {
compile 'com.github.bumptech.glide:okhttp-integration:1.5.0@aar'
compile 'com.squareup.okhttp:okhttp:2.7.5'
}
使用Volley来作为HTTP通讯组件的配置如下:
dependencies {
compile 'com.github.bumptech.glide:volley-integration:1.5.0@aar'
compile 'com.mcxiaoke.volley:library:1.0.19'
}
Glide配置和自定义模块就介绍到此。