美文网首页Android技术知识Android学习
Glide 系列(八) Glide配置和自定义模块

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

作者: 嘎啦果安卓兽 | 来源:发表于2018-03-30 13:59 被阅读1455次

    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中进行配置
    在<application>标签下加入

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

    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 <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();
            }
        }
    
    GenericLoaderFactory类下面有两个map
    • Map<Class, Map<Class, ModelLoaderFactory>> 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<T, Y> {
        DataFetcher<Y> 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<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());

    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);

    public class ByteArrayFetcher implements DataFetcher<InputStream> {
         ... ...
        @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<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作网络请求的一个过程。

    public class HttpUrlFetcher implements DataFetcher<InputStream> {
        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<String, String> 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<String, String> 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<GlideModule> 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配置和自定义模块

        本文链接:https://www.haomeiwen.com/subject/whhicftx.html