Glide框架设计<三>-------图片资源加载继续手写实现、Glide框架架构源码分析
图片资源加载手写:
FileUriLoader:
跟着上一次https://www.cnblogs.com/webor2006/p/12322227.html的代码继续编写,回忆一下:
已经实现了对于网络图片的加载,好,接下来咱们再来拓展一下,从文件中来加载图片,像这样:
有了标准行为,来实现一下ModelLoader既可:
package com.android.glidearcstudy.glide.load.model; import android.content.ContentResolver; import android.net.Uri; import com.android.glidearcstudy.glide.load.ObjectKey; import com.android.glidearcstudy.glide.load.model.data.FileUriFetcher; import java.io.InputStream; public class FileUriLoader implements ModelLoader<Uri, InputStream> { private final ContentResolver contentResolver; public FileUriLoader(ContentResolver contentResolver) { this.contentResolver = contentResolver; } @Override public boolean handles(Uri uri) { return ContentResolver.SCHEME_FILE.equalsIgnoreCase(uri.getScheme()); } @Override public LoadData<InputStream> buildData(Uri uri) { return new LoadData<>(new ObjectKey(uri), new FileUriFetcher(uri, contentResolver)); } }
接下来使用一下:
StringModelLoader:
有可能还有这种情况:
所以再来创建一个Loader来处理这种情况:
package com.android.glidearcstudy.glide.load.model; import android.net.Uri; import java.io.File; import java.io.InputStream; public class StringModelLoader implements ModelLoader<String, InputStream> { private final ModelLoader<Uri, InputStream> loader; public StringModelLoader(ModelLoader<Uri, InputStream> loader) { this.loader = loader; } @Override public boolean handles(String s) { return true; } @Override public LoadData<InputStream> buildData(String model) { Uri uri; if (model.startsWith("/")) { uri = Uri.fromFile(new File(model)); } else { uri = Uri.parse(model); } return loader.buildData(uri); } }
其使用如下:
各种类型Loader汇总封装:比较巧妙
目前咱们已经封装了三种类型的Loader了:
那如果我们使用的话还是根据实际你用的是哪种地址来自己判断用哪个一个Loader,显示这种使用方式对于用户来讲很麻烦,所以接下来咱们准备将其所有的Loader汇总一下,然后减化调用的复杂度,具体做法如下,先弄一个Loader注册器:
package com.android.glidearcstudy.glide.load.model; public class ModelLoaderRegistry { /** * 注册 Loader * * @param modelClass 数据来源类型 String File * @param dataClass 数据转换后类型 加载后类型 String/File->InputStream * @param factory 创建ModelLoader的工厂 */ public synchronized <Model, Data> void add(Class<Model> modelClass, Class<Data> dataClass, ModelLoader.ModelLoaderFactory<Model, Data> factory) { //TODO } }
那定义这个怎么调用呢?如下:
其中第三个参数是一个工厂:
这里我们在具体的Loader中来定义其实现类:
此时咱们调用时就可以这样了:
好,其它Loader则依葫芦画瓢,都定义好工厂:
接下来咱们又可以注册这种Loader到注册器里面了,如下:
最后还剩一个StringModelLoader,这里先暂且不定义工厂,先来对咱们的注册器进行完善了之后再来定义它,回到注册器:
package com.android.glidearcstudy.glide.load.model; import java.util.ArrayList; import java.util.List; public class ModelLoaderRegistry { private List<Entry<?, ?>> entries = new ArrayList<>(); /** * 注册 Loader * * @param modelClass 数据来源类型 String File * @param dataClass 数据转换后类型 加载后类型 String/File->InputStream * @param factory 创建ModelLoader的工厂 */ public synchronized <Model, Data> void add(Class<Model> modelClass, Class<Data> dataClass, ModelLoader.ModelLoaderFactory<Model, Data> factory) { entries.add(new Entry<>(modelClass, dataClass, factory)); } private static class Entry<Model, Data> { Class<Model> modelClass; Class<Data> dataClass; ModelLoader.ModelLoaderFactory<Model, Data> factory; public Entry(Class<Model> modelClass, Class<Data> dataClass, ModelLoader.ModelLoaderFactory<Model, Data> factory) { this.modelClass = modelClass; this.dataClass = dataClass; this.factory = factory; } boolean handles(Class<?> modelClass, Class<?> dataClass) { // A.isAssignableFrom(B) B和A是同一个类型 或者 B是A的子类 return this.modelClass.isAssignableFrom(modelClass) && this.dataClass.isAssignableFrom(dataClass); } boolean handles(Class<?> modelClass) { // A.isAssignableFrom(B) B和A是同一个类型 或者 B是A的子类 return this.modelClass.isAssignableFrom(modelClass); } } }
好,接下来增加一个查找方法:
为啥有时会查出来多个呢?其实很容易理解,比如看:
这两种类型的加载方式的<model,data>是不是都是<String,InputStream>?当然是的呀,所以说需要对于这种会查出来多个的情况进行处理,如下:
package com.android.glidearcstudy.glide.load.model; import java.util.List; public class MultiModelLoader<Model, Data> implements ModelLoader<Model, Data> { //FileUriModelLoader HttpUriModelLoader private final List<ModelLoader<Model, Data>> modelLoaders; public MultiModelLoader(List<ModelLoader<Model, Data>> modelLoaders) { this.modelLoaders = modelLoaders; } @Override public boolean handles(Model model) { for (ModelLoader<Model, Data> modelLoader : modelLoaders) { if (modelLoader.handles(model)) { return true; } } return false; } @Override public LoadData<Data> buildData(Model model) { for (int i = 0; i < modelLoaders.size(); i++) { ModelLoader<Model, Data> modelLoader = modelLoaders.get(i); // Model=>Uri:http if (modelLoader.handles(model)) { LoadData<Data> loadData = modelLoader.buildData(model); return loadData; } } return null; } }
好,此时还剩下StringModelLoader还木有定义Factory,此时就可以来定义了,因为它其实是需要从注册器中查找现有的Loader来实现加载既可,如下:
此时就中以再给注册器中增加这么一种Loader类型了:
另外在注册器中还需要根据modelClass类型来获取对应Loader的方法,如下:
此时咱们就可以这样使用了:
ModelLoaderRegistry loaderRegistry = new ModelLoaderRegistry(); //先注册所有加载类型 loaderRegistry.add(Uri.class, InputStream.class, new HttpUriLoader.Factory()); loaderRegistry.add(Uri.class, InputStream.class, new FileUriLoader.Factory(context.getContentResolver())); loaderRegistry.add(String.class, InputStream.class, new StringModelLoader.StreamFactory()); //然后再根据加载类型来获取具体的模型加载器 List<ModelLoader<String, ?>> modelLoaders = loaderRegistry.getModelLoaders(String.class); ModelLoader<String, ?> modelLoader = modelLoaders.get(0); //HttpUriFetcher final ModelLoader.LoadData<InputStream> loadData2 = (ModelLoader.LoadData<InputStream>) modelLoader.buildData("https://ss1.bdstatic" + ".com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2669567003," + "3609261574&fm=27&gp=0.jpg22222222asads"); new Thread() { @Override public void run() { loadData2.fetcher.loadData(new DataFetcher.DataFetcherCallback<InputStream>() { @Override public void onFetcherReady(InputStream o) { try { Log.e(TAG, "ready:" + o.available()); } catch (IOException e) { e.printStackTrace(); } } @Override public void onLoadFaled(Exception e) { e.printStackTrace(); } }); } }.start();
这样对于调用方而言就不需要做任何类型的判断来选择不同的Loader了,一切都由注册器中来完成了,大大的减化了调方者的使用难度。至此,关于加载这块的逻辑就写到这了。
Glide框架源码分析---加载原理:
接下来则不是手写了,而是分析Glide主流程的源码【这里分析的也是本地已经手写实现了的,有删减,把这些分析清楚那手写也妥妥的】,这块的内容实在是太多了,能把整个Glide的框架的原理搞清楚也是挺不容易的,先来贴一张整体的加载流程时序图:
大图附件地址:https://files.cnblogs.com/files/webor2006/Glide%E5%8A%A0%E8%BD%BD%E5%8E%9F%E7%90%86.jpg.zip
从这时序图就可以看出来Glide的逻辑是有多复杂,所以接下来从我们使用的角度并对着上面的时序图一点点来深入了解它的调用流程。
Glide.with():获取缓存中最大的内存
此时先来分析一下它:
此时就做了一些初始化工作,细看一下:
接下来看一下整个构建的细节:
看一下获取最大缓存大小是如何计算的?
哦,原来是这样来计算最大缓存空间大小的,好,继续回到build的流程中往下分析:
看一下LruArrayPool是个什么东东:
好,继续往下来分析构建细节:
内存分配好之后,接下来则来初始化各个缓存,就是之前我们所手写过的,如下:
RequestManagerRetriever:
接下来继续回到主流程往下:
看一下它是在哪实例化的:
接下来:
也就是根据不同的Context来做处理,先来看一下假如是Application 的Context的话则会执行:
如注释所说,如果是给Application使用的,那就没必要对生命周期进行管理了,全局缓存着,而如果是像Activity或Fragment的呢?
发现居然New出了一个SupportRequestManagerFragment了。。难道Glide生命周期是用Fragment来管理的,难道弄了个隐藏的Fragment??好逆天的感觉,先来看一下这个Fragment:
还真是耶~~其中我们再看个细节:
握草,牛逼了,还真是提交了个空的Fragment,最后再来看一下生命周期回调方法的大体实现:
package com.dn_glide.myapplication.glide.manager; import java.util.Collections; import java.util.Set; import java.util.WeakHashMap; /** * 生命周期监听管理器 */ class ActivityFragmentLifecycle implements Lifecycle { // private final Set<LifecycleListener> lifecycleListeners = Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>()); //已启动 private boolean isStarted; //已销毁 private boolean isDestroyed; @Override public void addListener(LifecycleListener listener) { lifecycleListeners.add(listener); if (isDestroyed) { listener.onDestroy(); } else if (isStarted) { listener.onStart(); } else { listener.onStop(); } } @Override public void removeListener(LifecycleListener listener) { lifecycleListeners.remove(listener); } void onStart() { isStarted = true; for (LifecycleListener lifecycleListener : lifecycleListeners) { lifecycleListener.onStart(); } } void onStop() { isStarted = false; for (LifecycleListener lifecycleListener : lifecycleListeners) { lifecycleListener.onStop(); } } void onDestroy() { isDestroyed = true; for (LifecycleListener lifecycleListener : lifecycleListeners) { lifecycleListener.onDestroy(); } } }
那该监听是在哪注册的呢?
好,关于生命周期的管理这里先大致看一下,在之后会专门再详细进行分析的,回到主流程继续往下分析:
Glide.with(this).load():
此时就构建请求了:
package com.dn_glide.myapplication.glide; import android.widget.ImageView; import com.dn_glide.myapplication.glide.request.Request; import com.dn_glide.myapplication.glide.request.RequestOptions; import java.io.File; public class RequestBuilder { private final GlideContext glideContext; private RequestOptions requestOptions; private RequestManager requestManager; private Object model; public RequestBuilder(GlideContext glideContext, RequestManager requestManager) { this.glideContext = glideContext; this.requestManager = requestManager; this.requestOptions = glideContext.defaultRequestOptions; } public RequestBuilder apply(RequestOptions requestOptions) { this.requestOptions = requestOptions; return this; } public RequestBuilder load(String string) { model = string; return this; } public RequestBuilder load(File file) { model = file; return this; } /** * 加载图片并设置到ImageView当中 * * @param view */ public void into(ImageView view) { //将View交给Target Target target = new Target(view); //图片加载与设置 Request request = new Request(glideContext,requestOptions, model, target); //Request交给 RequestManager 管理 requestManager.track(request); } }
Glide.with(this).load().into(View):
直接在请求体中看into()的实现:
这里只看核心的交由RequestManager管理的逻辑:
跟进去:
也就是目前分析到了这了:
好,暂且这次先分析到这,篇幅有限,下次接着再继续分析。