图片是承载数据内容最直接的方式,对于客户端而言图片加载无疑是非常重要的,因此我们有必要去了解部分图片加载框架实现。Glide图片框架用起来很方便,接口调用都简单易懂,加载图片也非常流畅。今天咱们来简单的研究下,它怎么处理的?
一,图片加载需考虑的问题
1,如何明确加载图片的时机? 只要网络请求就立马加载?
2,图片加载如何异步?
3,图片加载是否有中间过度状态?
4,图片加载后是否有需要干预处理图片?
5,图片缓存的使用
那我们来分析:首先图片加载的时机,我们肯定是期望页面前端展示交互的时候去处理,如果当前页面关闭了或者屏幕熄灭(这些都是不期望继续去加载的,当然得释放资源做它期望做的事情)。第二个问题:图片本身就是耗时的处理,网络,IO等资源调度。应该是通过线程池调度资源。至于第三个问题,针对一个好的交互,无疑是需要要中间状态(比如说默认图等)去缓解网络处理过程的延时压力,使得过度更为平滑舒适。第四,图片加载完后,我们可能需要对图片进一步加工,比如说裁切等等处理,因此很有必要留有空间对已下载好的图片进一步处理。第五,缓存图片使用,减少不必要网络开销。
带着这些考量我们看看Glide怎么做的?
带着这些考量我们看看Glide怎么做的?
二,Glide使用
1,了解Glide基本模型
首先,所有图片调用,都是依赖requestManager进行处理,它主要包含requestTracker(请求分发控制), LifeCycle对生命周期管理,同时负责构建实际请求(包括请求链接,图片默认图,图片转化,图片错误图,目标作用视图等等)。
其次,Lifecycle实际是根据当前组件的周期,同步通知给需要该变化观察者RequestManager, Target
再者,构建GenericRequestBuilder后进一步产生实际请求GenericRequest(真实处理图片加载能力的地方)。
2, 具体Glide如何处理上述问题
(1) 加载图片时机-lifecycle控制
我们肯定是需要将加载时机跟当前组件的生命周期建立联系。因此当组件的生命周期发生变化的时候,我们就能进一步对图片请求进行控制。
Glide中就通过Context去产生相应的联系。如果Context为Activity/FragmentActivity,那么就通过FragmentManager创建委托Fragment,通过当前Fragment的周期变化同步通知。如果Context是Application,那么就通过Application的生命周期回调,委托通知相应变化。
(2)图片加载过程处理分析
(2)图片加载过程处理分析
我们知道图片加载是受生命周期控制,那么我们肯定在可见的时候去加载,前面我们知道requestMananger作为请求管理器,同时本身也是生命周期变化的观察者。因此,我们可以认识到,requestManager第一时间监测到生命周期发生变化的时候,就会去处理了,这也应该是请求实际执行的源头。
通过以上这个流程大致理清一下思路。
首先图片加载受生命周期控制,
生命周期开始时会触发RequestManager.onStart,此时就会调用requestTracker去分发已经登记的request请求。
request.begin开始处理请求,由于是图片加载,我们需要知道 当前目标尺寸的信息后方进一步加载资源。于此同时 通知目标即将开始加载了,target.onLoadStart就会处理一些预设资源(如默认图)
request.begin开始处理请求,由于是图片加载,我们需要知道 当前目标尺寸的信息后方进一步加载资源。于此同时 通知目标即将开始加载了,target.onLoadStart就会处理一些预设资源(如默认图)
接下来,request会让引擎
engine去分配执行任务EnginJob,任务实际执行体是enginRunnable(
request也会注册资源加载结果监听)(注:引擎执行任务前会先从内存缓存,和当前活跃资源中读取,如果没有才会分配任务EnginJob))。
任务enginjob预先会从通过
磁盘线程池diskcacheservice去从磁盘缓存加载,加载过程依赖DecoderJob处理。
若磁盘加载失败则会在
失败回调中进一步调用从网络资源调度线程池sourceService加载,加载成功会会将加载资源通过
回调层层传递给target,去实现资源刷新。
了解了以上流程,我们再来关注之前提出的问题。
了解了以上流程,我们再来关注之前提出的问题。
异步加载:glide加载过程中,分别开启了磁盘读取线程池和网络加载线程池去处理加载过程的耗时操作。
加载中间状态:在图片加载之初,会先通知target开始加载,以便target更新默认图等等行为。
缓存: glide在engin执行过程中,会先从自己内存缓存和记录的活跃资源中获取,耗时iO和网络才开辟线程池调度。
缓存: glide在engin执行过程中,会先从自己内存缓存和记录的活跃资源中获取,耗时iO和网络才开辟线程池调度。
图片加载后处理: 以上流程图并没有细化,图片加载后会调用transform去对图片进一步转化,因此我们可以定义transform规则,对图片进行裁切处理。
3,Glide补充
(1) glide 定制:我们通常调用glide都只用Api中,Glide.with(context).load().... 显然,框架内部已经为我们设置好了必要参数。那么我们如何去进一步定制?
(1) glide 定制:我们通常调用glide都只用Api中,Glide.with(context).load().... 显然,框架内部已经为我们设置好了必要参数。那么我们如何去进一步定制?
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; }
参考源码: glide实际创建过程会先通过ManifestParser解析配置中GlideModule, 这个GlideModule是用户自定义的
<meta-data android:name="*.**.SpecialGlideModule" android:value="GlideModule"/>
在当前可以在applyOptions回调,自定义GlideBuilder相关属性拓展(如定义MemoryCache, BitmapPool等等)
同时会将当前Module注册。
(2)
关于ModelLoader: 初始对这个模型有点晕,很多地方都贯穿了ModelLoader的概念,而且注册的时候一大堆索引参数,modelClass, resourceClass。
先从字面理解:模型加载器,肯定针对某种特定模型定义的加载器。包含接口getResourceFetcher,也是获取资源调度器的意思。
先从字面理解:模型加载器,肯定针对某种特定模型定义的加载器。包含接口getResourceFetcher,也是获取资源调度器的意思。
看下注册的位置:
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的时候,默认会根据模型类型,和资源类型注册对应的Loader,这样在处理的时候,会查找到对应加载器进行处理。同时我们可以自定义加载器覆盖以上的,比如说HttpUrlGlideUrlLoader,我们可以定义okHttp相关的去覆盖它(当然覆盖前提,必须要moduleClass和resourceClass一样:GlideUrl.class,InputStream.class )
于此同时,Loader使用有时候是嵌套代理处理的。如StreamStringLoader实际依赖代理StreamUriLoader,而StreamUriLoader依赖HttpUrlGlideUrlLoader处理。
public class StreamStringLoader extends StringLoader<InputStream> implements StreamModelLoader<String> { /** * The default factory for {@link com.bumptech.glide.load.model.stream.StreamStringLoader}s. */ public static class Factory implements ModelLoaderFactory<String, InputStream> { @Override public ModelLoader<String, InputStream> build(Context context, GenericLoaderFactory factories) { return new StreamStringLoader(factories.buildModelLoader(Uri.class, InputStream.class)); }
public class StreamUriLoader extends UriLoader<InputStream> implements StreamModelLoader<Uri> { /** * THe default factory for {@link com.bumptech.glide.load.model.stream.StreamUriLoader}s. */ public static class Factory implements ModelLoaderFactory<Uri, InputStream> { @Override public ModelLoader<Uri, InputStream> build(Context context, GenericLoaderFactory factories) { return new StreamUriLoader(context, factories.buildModelLoader(GlideUrl.class, InputStream.class)); }
注: factories.buildModelLoader实际就是查询现注册的ModelLoader, 索引条件就是后面参数,我们可以在前面register中得到结果。