Picasso 源码 学习(一) 图片加载流程
Picasso地址 http://square.github.io/picasso/
最简单的使用方法
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
大概流程
1、Picasso 使用with(Context context)方法创建一个单例对象。
public static Picasso with(@NonNull Context context) {if (context == null) {throw new IllegalArgumentException("context == null");}if (singleton == null) {synchronized (Picasso.class) {if (singleton == null) {singleton = new Builder(context).build();}}}return singleton;}
2、Picasso默认帮我们初始化好了默认的下载器、图片的缓存等对象,在这里我们可以自己对这些功能进行自定义
public Picasso build() {Context context = this.context;if (downloader == null) {downloader = Utils.createDefaultDownloader(context);}if (cache == null) {//Picasso自定义的图片缓存,设置app RAM大小的15%作为图片内存缓存空间cache = new LruCache(context);}if (service == null) {//通过手机网络情况对线程池大小进行设置,默认有3个线程,当手机网络状态改变的时候则用广播接受消息并重新设置线程池大小,wifi为4个,4G为3个,3G为2个,2G为一个service = new PicassoExecutorService();}if (transformer == null) {//在请求服务器前对请求做一些处理,例如,如果你想用CDN,可以更改hostname,从CDN上获取图片,默认为空实现transformer = RequestTransformer.IDENTITY;}//初始化统计器Stats stats = new Stats(cache);//用来转发图片请求,暂停,取消,重试等操作,//注意参数列表里面有一个handler,这是用主线程创建的handler,当图片下载处理完成后,用来更新UI的//Dispatcher自己也有一个handler,是子线程的handler,Dispatcher是用自己的handler来进行的上述转发操作Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,defaultBitmapConfig, indicatorsEnabled, loggingEnabled);}
初始化下载器downloader = Utils.createDefaultDownloader(context)
通过反射看引用项目是否存在okhttp的jar包,如果存在则使用okhttp来进行图片的下载,如果没有则使用UrlConnection。
static Downloader createDefaultDownloader(Context context) {//sdk>=9 判断项目是否存在okhttp的jar包if (SDK_INT >= GINGERBREAD) {try {Class.forName("okhttp3.OkHttpClient");return OkHttp3DownloaderCreator.create(context);} catch (ClassNotFoundException ignored) {}try {Class.forName("com.squareup.okhttp.OkHttpClient");return OkHttpDownloaderCreator.create(context);} catch (ClassNotFoundException ignored) {}}return new UrlConnectionDownloader(context);}
okhttp下载器或者UrlConnection的下载器,okhttpclient或者UrlConnection的初始化、获取服务器响应数据流并对数据缓存的设置,缓存的大小为app CacheDir空间的2%
3、加载图片 load(“http://i.imgur.com/DvpvklR.png“)
方法名看起来是像在加载图片,其实内部是将uri保存在Request中,并返回一个RequestCreator对象。这里需要区分下RequestCreator和Request的区别,RequestCreator是构造一个图片下载请求的类,例如给imageview设置placeholder图片、error图片,imageview宽高为wrap_content需设置的fit()方法、给图片指定大小的resize(width,height)方法、设置tag等,这些方法都是返回的RequestCreator对象,方便我们来根据需求来构造图片的属性。而Request类是Picasso用来保存图片信息的,保存图片的uri(或者resourceId)、Bitmap.Config、图片被设置的width和height、rotation等信息。load方法里的path可以是本地图片的URI地址、网络图片的URL地址、资源文件中的图片id
public RequestCreator load(@Nullable String path) {if (path == null) {return new RequestCreator(this, null, 0);}if (path.trim().length() == 0) {throw new IllegalArgumentException("Path must not be empty.");}return load(Uri.parse(path));}
public RequestCreator load(@Nullable Uri uri) {return new RequestCreator(this, uri, 0);}
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对象的同时,创建一个Request.Builder对象
Builder(Uri uri, int resourceId, Bitmap.Config bitmapConfig) {this.uri = uri;this.resourceId = resourceId;this.config = bitmapConfig;}
例如我们使用RequestCreator中的resize方法,则是通过this.data这个Request.Builder对象来设置大小
public RequestCreator resize(int targetWidth, int targetHeight) {data.resize(targetWidth, targetHeight);return this;}
public Builder resize(@Px int targetWidth, @Px int targetHeight) {if (targetWidth < 0) {throw new IllegalArgumentException("Width must be positive number or 0.");}if (targetHeight < 0) {throw new IllegalArgumentException("Height must be positive number or 0.");}if (targetHeight == 0 && targetWidth == 0) {throw new IllegalArgumentException("At least one dimension has to be positive number.");}this.targetWidth = targetWidth;this.targetHeight = targetHeight;return this;}
4、下载、处理、显示图片into(ImageView target)
在上面的使用方法中,into只有imageview这个参数,没有Callback,Callback是图片加载成功或者失败的回调,这里的callback是空值,则表明图片显示成功或者 失败都不会有回调方法执行
public void into(ImageView target, Callback callback) {long started = System.nanoTime();//检查是否在主线程进行的操作checkMain();if (target == null) {throw new IllegalArgumentException("Target must not be null.");}//如果在load方法中没有传递uri或者资源id,则取消这一次请求,并且如果设置了默认占位图,则在这个imageview上显示占位图if (!data.hasImage()) {picasso.cancelRequest(target);if (setPlaceholder) {setPlaceholder(target, getPlaceholderDrawable());}return;}//如果imageview没有设置宽高,需使用fit()方法来对图片进行大小计算,//设置了fit()后deferred为true,注意fit方法和resize方法不能共用if (deferred) {if (data.hasSize()) {throw new IllegalStateException("Fit cannot be used with resize.");}int width = target.getWidth();int height = target.getHeight();//如果是在oncreate()方法中加载图片,此时viewtree还完成测量,所以通过getWidth、getHeight方法获取imageview的宽高是为0的if (width == 0 || height == 0) {if (setPlaceholder) {setPlaceholder(target, getPlaceholderDrawable());}//这里的new DeferredRequestCreator实现了ViewTreeObserver的OnPreDrawListener接口来添加绘制完成的监听,当viewtree测量完成后开始绘制前会回调onPreDraw()方法//onPreDraw()重新获得imageview的宽高后再执行 this.creator.unfit().resize(width, height).into(target, callback);picasso.defer(target, new DeferredRequestCreator(this, target, callback));return;}data.resize(width, height);}Request request = createRequest(started);String requestKey = createKey(request); //为每个资源请求创建一个key,用来对应不同的bitmap//如果内存中有已缓存的bitmap,取消本次请求并为imageview设置bitmap并显示if (shouldReadFromMemoryCache(memoryPolicy)) {Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);if (bitmap != null) {picasso.cancelRequest(target);setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);if (picasso.loggingEnabled) {log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);}if (callback != null) {callback.onSuccess();}return;}}if (setPlaceholder) {setPlaceholder(target, getPlaceholderDrawable());}//Action中包含request、target(imageview)、requestkey,后续的流程用action来与下载线程进行绑定,//防止异步下载图片后,在listview的缓存机制下产生图片显示错乱的问题Action action =new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,errorDrawable, requestKey, tag, callback, noFade);picasso.enqueueAndSubmit(action);}
5、提交请求
void enqueueAndSubmit(Action action) {Object target = action.getTarget();//targetToAction是一个WeakHashMap//判断该target(imageview)是否有与target绑定的action在执行,如果有则取消上次请求if (target != null && targetToAction.get(target) != action) {// This will also check we are on the main thread.这里写图片描述cancelExistingRequest(target);targetToAction.put(target, action);}submit(action);}
submit(action)方法通过dispatcher转发请求执行dispatcher.performSubmit(action)方法
void performSubmit(Action action, boolean dismissFailed) {//因为action绑定了tag,如果在使用Picasso的时候设置了.tag()方法,则可以通过tag来判断是否暂停了请求//例如在listview中,用户在不断滑动,那么可以在用户滑动的过程中使用Picasso.pauseTag(tag)来暂停请求,在停止滑动后恢复请求if (pausedTags.contains(action.getTag())) {pausedActions.put(action.getTarget(), action);if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),"because tag '" + action.getTag() + "' is paused");}return;}//BitmapHunter是一个获取bitmap的线程,获取图片,解码、压缩图片//hunterMap是用来保存正在执行中的hunter,如果有正在进行的,则returnBitmapHunter hunter = hunterMap.get(action.getKey());if (hunter != null) {hunter.attach(action);return;}//判断PicassoExecutorService线程池是否关闭if (service.isShutdown()) {if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");}return;}//forRequest在下面hunter = forRequest(action.getPicasso(), this, cache, stats, action);hunter.future = service.submit(hunter);hunterMap.put(action.getKey(), hunter);if (dismissFailed) {failedActions.remove(action.getTarget());}if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());}}
下面是forRequest(action.getPicasso(), this, cache, stats, action)的代码片段
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,Action action) {Request request = action.getRequest();List<RequestHandler> requestHandlers = picasso.getRequestHandlers();// Index-based loop to avoid allocating an iterator.//noinspection ForLoopReplaceableByForEachfor (int i = 0, count = requestHandlers.size(); i < count; i++) {//RequestHandler有几种请求策略,从网络请求,请求本地图片,从资源文件中请求等RequestHandler requestHandler = requestHandlers.get(i);//通过判断URI的scheme来判断使用哪种请求方式//例如网络请求判断 return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));if (requestHandler.canHandleRequest(request)) {return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);}}return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);}
回到上面,之后hunter.future = service.submit(hunter) 线程池执行获取bitmap的线程,BitmapHunter中的run方法如下
@Override public void run() {try {updateThreadName(data);if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));}result = hunt();if (result == null) {dispatcher.dispatchFailed(this);} else {//当result不为空则转发成功操作dispatcher.dispatchComplete(this);}} catch (Downloader.ResponseException e) {if (!e.localCacheOnly || e.responseCode != 504) {exception = e;}dispatcher.dispatchFailed(this);} catch (NetworkRequestHandler.ContentLengthException e) {exception = e;//重试dispatcher.dispatchRetry(this);} catch (IOException e) {exception = e;dispatcher.dispatchRetry(this);} catch (OutOfMemoryError e) {StringWriter writer = new StringWriter();stats.createSnapshot().dump(new PrintWriter(writer));exception = new RuntimeException(writer.toString(), e);dispatcher.dispatchFailed(this);} catch (Exception e) {exception = e;dispatcher.dispatchFailed(this);} finally {Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);}}
Bitmap hunt() throws IOException {Bitmap bitmap = null;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;}}data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;//这里我们是请求网络的图片,所以requestHandler.load是执行NetworkRequestHandler中的load方法//返回的Result包含bitmap和流信息RequestHandler.Result result = requestHandler.load(data, networkPolicy);if (result != null) {loadedFrom = result.getLoadedFrom();exifOrientation = 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大小,防止oombitmap = decodeStream(is, data);} finally {Utils.closeQuietly(is);}}}if (bitmap != null) {if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_DECODED, data.logId());}stats.dispatchBitmapDecoded(bitmap);//统计解码bitmapif (data.needsTransformation() || exifOrientation != 0) {synchronized (DECODE_LOCK) {if (data.needsMatrixTransform() || exifOrientation != 0) {bitmap = transformResult(data, bitmap, exifOrientation);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) {stats.dispatchBitmapTransformed(bitmap);}}}return bitmap;}
dispatcher转发后执行如下操作
void performComplete(BitmapHunter hunter) {//判读是否需要缓存if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {cache.set(hunter.getKey(), hunter.getResult());}//获取图片成功移除hunterMap中的hunterhunterMap.remove(hunter.getKey());//batch(hunter);if (hunter.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");}}
//批量更新UI
private void batch(BitmapHunter hunter) {if (hunter.isCancelled()) {return;}batch.add(hunter);if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);}}
void performBatchComplete() {List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);batch.clear();mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));logBatch(copy);}
主线程的handler中进行UI更新通知
case HUNTER_BATCH_COMPLETE: {@SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;//noinspection ForLoopReplaceableByForEachfor (int i = 0, n = batch.size(); i < n; i++) {BitmapHunter hunter = batch.get(i);hunter.picasso.complete(hunter);}break;}
void complete(BitmapHunter hunter) {Action single = hunter.getAction();List<Action> joined = hunter.getActions();boolean hasMultiple = joined != null && !joined.isEmpty();boolean shouldDeliver = single != null || hasMultiple;if (!shouldDeliver) {return;}Uri uri = hunter.getData().uri;Exception exception = hunter.getException();Bitmap result = hunter.getResult();LoadedFrom from = hunter.getLoadedFrom();if (single != null) {deliverAction(result, from, single);}if (hasMultiple) {//noinspection ForLoopReplaceableByForEachfor (int i = 0, n = joined.size(); i < n; i++) {Action join = joined.get(i);deliverAction(result, from, join);}}if (listener != null && exception != null) {listener.onImageLoadFailed(this, uri, exception);}}
private void deliverAction(Bitmap result, LoadedFrom from, Action action) {if (action.isCancelled()) {return;}if (!action.willReplay()) {targetToAction.remove(action.getTarget());}if (result != null) {if (from == null) {throw new AssertionError("LoadedFrom cannot be null.");}//在into方法中,我们初始化了一个ImageViewAction,这个action一直就是在各个方法中出现的actionaction.complete(result, from);if (loggingEnabled) {log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);}} else {action.error();if (loggingEnabled) {log(OWNER_MAIN, VERB_ERRORED, action.request.logId());}}}
ImageViewAction中的complete方法
@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {if (result == null) {throw new AssertionError(String.format("Attempted to complete action with no result!\n%s", this));}ImageView target = this.target.get();if (target == null) {return;}Context context = picasso.context;boolean indicatorsEnabled = picasso.indicatorsEnabled;PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);if (callback != null) {callback.onSuccess();}}
到这里,执行Picasso.with(context).load(“http://i.imgur.com/DvpvklR.png“).into(imageView)方法后,imageView上便显示好图片了,下面我们再看看流程图和类图
Picasso 源码 学习(一) 图片加载流程相关推荐
- JVM源码阅读-本地库加载流程和原理
前言 本文主要研究OpenJDK中JVM源码中涉及到native本地库的加载流程和原理的部分.主要目的是为了了解本地库是如何被加载到虚拟机,以及是如何找到并执行本地库里的本地方法,以及JNI的 JNI ...
- 【Spring源码分析】Bean加载流程概览
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
- beaninfo详解源码解析 java_【Spring源码分析】Bean加载流程概览
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
- Spring源码分析:Bean加载流程概览及配置文件读取
很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...
- 从源码解析-结合Activity加载流程深入理解ActivityThrad的工作逻辑
ActivityThread源码解析 前言 类简称 类简介 一 二 三 四 五 代理和桩的理解 ActivityThread ActivityThread.main AT.attach AMN.get ...
- 【框架源码】Spring源码解析之BeanDefinition加载流程解析
观看本文之前,我们先思考一个问题,Spring是如何描述Bean对象的? Spring是根据BeanDefinition来创建Bean对象,BeanDefinition就是Spring中表示Bean定 ...
- Glide图片加载流程浅析
Glide是Android开发中常用的图片框架,其最基本用法例如Glide.with(context).load(url).into(imageView),我们沿着此链式调用的顺序一窥Glide图片加 ...
- 从源码分析Android的Glide库的图片加载流程及特点
转载:http://m.aspku.com/view-141093.html 这篇文章主要介绍了从源码分析Android的Glide库的图片加载流程及特点,Glide库是Android下一款人气很高的 ...
- Glide 4.9源码解析-图片加载流程
本文Glide源码基于4.9,版本下载地址如下:Glide 4.9 前言 由于Glide源码真的很复杂,因此本文只分析和贴出与图片加载流程相关的功能以及代码.另外本文Glide源码基于4.9,与3.x ...
- Android 11.0 Settings源码分析 - 主界面加载
Android 11.0 Settings源码分析 - 主界面加载 本篇主要记录AndroidR Settings源码主界面加载流程,方便后续工作调试其流程. Settings代码路径: packag ...
最新文章
- Linux环境编程--文件基本操作
- 手把手教你熟悉Git操作
- linux系统运维费用,一般Linux运维学习的费用是多少?Linux学习
- python输出数据到excel-如何使用python将传感器数据输出保存到excel中
- UIScrollView点击StatusBar返回顶部失效的解决
- Route Flap Dampening
- 在Oracle中如何让SELECT查询绕过UNDO
- android查看kernel log
- 方舟编译器服务器端Java怎么用_方舟编译器可以运行Hello World了!
- ASP.NET Core + Angular 2 Template for Visual Studio
- AtCoder Beginner Contest 183 总结
- oracle 经纬度算距离,根据经纬度诀别用java和Oracle存储过程计算两点距离
- 阿里人工智能实验室招聘计算机视觉算法专家-交互图像方向
- iPhone 12 mini续航尿崩:玩游戏两小时就歇菜
- 2019蓝桥杯Java决赛题答案_2019-01-15 第八届蓝桥杯javaB组第二题
- 简单的dp(dp专题)
- jsf面试题_JSF面试问答
- Xshell连接centOS7与CentOS7联网——一步到位
- OSSIM安装zabbix
- DTW(动态时间归整)算法的前世今生
热门文章
- Swarm容器集群管理安装
- 【C语言中清空文件的方法】
- linux 下生成docx,linux下创建、删除文件和文件夹命令.docx
- 启动idea后,打开项目不久后闪退
- 软件开发委托(单位)协议
- dnf 跨服 服务器 位置,《DNF》86版本跨区表一览 看你属于哪个跨区频道
- 2012年9月 吉林大学计算机相关专业招聘会信息汇总
- HDU2717 Catch That Cow ( BFS )
- 华三交换机配置access命令_H3C交换机基本配置命令明细一览
- 右移位和除以2(C++)