Picasso 源码流程分析
2016-05-15 20:29
288 查看
前言
Picasso 是非常好用的图像加载框架,它清晰的图片加载流程:with->load->into,即使用当前上下文加载指定路径的图片资源,然后显示在 ImageView 中。所以,分析 Picasso 的源码,可以从它的流程着手。用法回顾:Picasso.with(context).load(uri).into(mImageView);
整体流程
一. with() 创建必备的对象创建 Downloader 对象,Utils.createDownloader(context);
创建 LruCache 对象
创建 ExecuteorService 线程池对象
创建 RequestTransfomer 对象
实例化 Diaptcher 对象,开启异步下载,开启缓存
实例化 Picasso 对象:执行监听、图形转换、调试模式等
二. load()
创建 RequestCreator对象
三. into()
创建 Request、Action 对象
交给 Dispatcher 执行
创建 BitmaHunter (用户捕获图片)
with 过程
首先在 with 方法内部使用 Builder 创建了单例的 Picasso 对象,而且这种单例是加上 volatile 关键字+双重检查加锁+ 懒汉式,懒汉式特点是:线程安全、高效、延迟加载(节约内存)。使用 volatile 关键字可以禁止 CPU 重排序优化、单例的修改对所有线程可见。此种写法,在 Android 系统不建议使用枚举的前提下,不失为一种较优解。static volatile Picasso singleton = null; public static Picasso with(Context context) { if (singleton == null) { synchronized (Picasso.class) { if (singleton == null) { singleton = new Builder(context).build(); } } } return singleton; }
接着,在 build 中初始化各种对象
/** Create the {@link Picasso} instance. */ public Picasso build() { Context context = this.context; // 创建默认下载器 if (downloader == null) { downloader = Utils.createDefaultDownloader(context); } // 默认的内存缓存算法(通过Lru 最近最少原则,进行内存的管理) if (cache == null) { cache = new LruCache(context); } // 创建Picasso自有的线程池对象(通过多线程加载网络图片) if (service == null) { service = new PicassoExecutorService(); } // 初始化自定义的Transmform对象(用户对图片的自定义转换) if (transformer == null) { transformer = RequestTransformer.IDENTITY; } // 创建了一个统计类 Stats stats = new Stats(cache); // 初始化调度者(需要传递:上下文、线程池、handler,下载器,缓存目录和统计类) Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats); // 最终利用 builder 对象的各种属性、对象,创建Picasso对象(需要传递:上下文、调度者、缓存目录、监听器、handler等,默认图片参数、是否开启调试标志、是否开启日志标志) return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats, defaultBitmapConfig, indicatorsEnabled, loggingEnabled); } }
Picasso 是如何初始化默认的 Downloader 的?
答案是:在 createDefaultDownloader 中,首先通过反射查找是否存在okHttpClient 对象,如果存在则使用 okHttpDownloader,否则使用 UrlConnectionDownloader
static Downloader createDefaultDownloader(Context context) { try { Class.forName("com.squareup.okhttp.OkHttpClient"); return OkHttpLoaderCreator.create(context); } catch (ClassNotFoundException ignored) { } return new UrlConnectionDownloader(context); }
在 Picasso 类中,还能清晰的看到定义调试模式的颜色
(当设置 Picasso 属性 indicatorsEnabled(true),加载后的 Bitmap 左上角会根据不同颜色指示图片来源):
来自内存:绿色
来自硬盘:蓝色
来自红色:网络
/** Describes where the image was loaded from. */ public enum LoadedFrom { MEMORY(Color.GREEN), DISK(Color.BLUE), NETWORK(Color.RED); final int debugColor; private LoadedFrom(int debugColor) { this.debugColor = debugColor; } }
load 过程
load(Uri) 返回的 RequestCreator 是一个请求创建者,可以对其设置各种属性。Picasso.load() 方法可以加载:File、resId、String 以及其他Uri类型的地址,除了 resId 单独作为参数交给 Request.Builder,其他的都被转换成了 Uri。RequestCreateor构造方法主要的作用:
传入了 Picasso 对象
使用 Request.Builder 创建了 Request 对象,后续对ReqestCreateor 的设置都作用在 Reqest 对象中
RequestCreator(Picasso picasso, Uri uri, int resourceId) { if (picasso.shutdown) { throw new IllegalStateException( "Picasso instance already shut down. Cannot submit new requests."); } this.picasso = picasso; this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig); }
RequestCreator 提供的各种设置方法:
public RequestCreator noPlaceholder() 不需要加载前显示的图片 public RequestCreator placeholder(int placeholderResId) 设置加载前显示的图片 public RequestCreator error(int errorResId) 加载出错时显示的图片 public RequestCreator tag(Object tag) 设置全局tag,以便对加载过程进行暂停、取消、继续的操作 public RequestCreator fit() 设置图片的显示模式为:填充ImageView控件宽高 public RequestCreator resize(int targetWidth, int targetHeight) 重新调整图片的大小 public RequestCreator centerCrop() 和 resize 方法配合使用,以resize前的尺寸作为参考,按比例修改图片尺寸 public RequestCreator centerInside() 和 resize 方法配合使用,以resize后的尺寸作为参考,将图片强制缩放到resize
into 过程
如果说 with、load 是 Picasso 图像加载的前戏, into 就是高潮:下载(hunt)、解析(decodeStream)。这个过程分散在以下几个区域。让我们一一解析。一、RequestCreater类中
1 . checkMain 检查当前线程是否是主线程
为啥要检查是否在主线程,因为通过分析可以发现,BitmapHunter 类下载完图片、解析完成后,会通过 handler message 机制向主线程发送消息,所以必须检查是否在主线程
2 . 设置图片的占位符、调整图片大小、图片裁切等
就是之前介绍的 RequestCreator 中的系列设置方法
3 . 创建 Request 对象
4 . 从内存中获取图片,获取到则直接返回
5 . 创建 ImageViewAction 对象
Picasso.enqueueActionSubmit(action) 提交请求
将上面的action对象提交到Picasso中了
二、Picasso 类中
1 . Picasso中submit(action);
2 . dispatcher.dispatcherSubmit(action);
上面说到,RequestCreator 对象中通过Picasso.enqueueActionSubmit(action) 将 action 提交到 Picasso 中,而 Picasso 中通过 dispatcher.dispatcherSubmit(action) 又将对象放入调度者中
三、Dispatcher类中
1 . handler.sendMessage()
2 . handler 类回调 handleMessage
3 . 创建 bitmaoHunter
上面说到,Picasso 中通过 dispatch.dispatchSubmit(action) 方法,将 action 提交到 dispatcher 调度者中,而调度者通过一个 handler message,将 action又重新发到 Picasso 中,最后在 Picasso 的 handler 的 handlerMessage 中创建了 BitmapHunter 对象。
四、BitmapHunter
1 . hunt 获取图片
2 . decodeStream 对图片进行解析
上面说到,Picasso 的handler 的 handleMessgae 中创建了BitmapHunter,而 BitmapHunter 将具体通过线程池下载和解析图片(hunt and decodeStream)
另外,BitmapHunter 实现了Runnable接口(就是一个会被添加到线程池ExecutorService 的一个任务),所有的加载、解析图片的过程都发生在子线程
hunt 的全过程:
Bitmap hunt() throws IOException { Bitmap bitmap = null; // 首先从内存缓存中加载图片(根据在RequestCreator中设置的memoryPolicy) if (shouldReadFromMemoryCache(memoryPolicy)) { bitmap = cache.get(key); if (bitmap != null) { stats.dispatchCacheHit(); loadedFrom = MEMORY; if (picasso.loggingEnabled) { log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache"); } return bitmap; } } // 网络加载图片(根据在RequestCreator中设定的NetworkPolicy) data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy; // 具体从网络读取的方法 RequestHandler.Result result = requestHandler.load(data, networkPolicy); if (result != null) { loadedFrom = result.getLoadedFrom(); exifRotation = result.getExifOrientation(); bitmap = result.getBitmap(); // If there was no Bitmap then we need to decode it from the stream. if (bitmap == null) { InputStream is = result.getStream(); try { bitmap = decodeStream(is, data); } finally { Utils.closeQuietly(is); } } } if (bitmap != null) { if (picasso.loggingEnabled) { log(OWNER_HUNTER, VERB_DECODED, data.logId()); } stats.dispatchBitmapDecoded(bitmap); if (data.needsTransformation() || exifRotation != 0) { synchronized (DECODE_LOCK) { if (data.needsMatrixTransform() || exifRotation != 0) { bitmap = transformResult(data, bitmap, exifRotation); if (picasso.loggingEnabled) { log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId()); } } if (data.hasCustomTransformations()) { bitmap = applyCustomTransformations(data.transformations, bitmap); if (picasso.loggingEnabled) { log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations"); } } } if (bitmap != null) { // 在这里根据用户自定义的图形转换规则,对Bitmap做进一步处理 stats.dispatchBitmapTransformed(bitmap); } } } // 加载、解析后返回被处理的btimap。至此,整个Picasso的异步加载过程完成 return bitmap; }
至此,整个过程算是完成了
附录
参考:1 . Android图片加载库Picasso源码分析
2 . picasso-强大的Android图片下载缓存库
相关文章推荐
- 递归
- 深度:Leap Motion手势识别大揭秘
- 构建之法阅读笔记05
- getopt
- Windows CTS环境的安装
- 韩信点兵(水题)
- URTracker事务跟踪系统
- pat L1-012. 计算指数
- IT-linux-shell-command--usleep
- Latex强制图片位置
- am命令的玩法
- iOS设计模式(一) 原型模式
- Android MediaScanner 多媒体扫描流程
- Android自定义搜索效果的实现
- HDU 3268 Columbus’s bargain(最短路)
- bzoj 2561(最小割)
- ==和equals方法及其区别
- Minimum Size Subarray Sum
- swift 2.2 语法 (上)
- 服务器有大量的time_wait进程怎么办?