Glide 系列(八) Glide配置和自定义模块

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.

Glide配置项图

具体代码是这样:

    @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可以注册的组件

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配置和自定义模块就介绍到此。

你可能感兴趣的:(Glide 系列(八) Glide配置和自定义模块)