Picasso源码解析
来源:互联网 发布:淘宝旺铺图片轮播尺寸 编辑:程序博客网 时间:2024/05/17 09:02
一、Picasso使用:
gradle:
compile 'com.squareup.picasso:picasso:2.5.2'
使用:
Picasso.with(this).load("http://...../photo3.jpg").into(myImg);
自定义的使用方法:
Picasso picasso = new Picasso.Builder(this) .memoryCache(new LruCache()) // 设置自定义的内存缓存 .addRequestHandler(requestHandler) // 设置自定义的RequestHandler .defaultBitmapConfig(bitmapConfig) // 设置自定义的Bitmap Config .downloader(okHttpDownloader) // 设置自定义的Downloader .executor(executorService) // 设置自定义的线程池 .requestTransformer(transformer) // 设置自定义的Transformor .listener(listener) // 添加Listener进行监听 .build();picasso.load(File/resId/Uri/String) .resize(width, height) .centerInside() .into(imageView);
二、源码分析
(一)Picasso.with进行一系列初始化
public static Picasso with(Context context) { if (singleton == null) { synchronized (Picasso.class) { if (singleton == null) { singleton = new Builder(context).build(); } } } return singleton;}
简单的多线程单例模式;同时使用Buidler模式来创建Picasso实例。
1、Picasso.Builder#build:
/** Start building a new {@link Picasso} instance. */public Builder(Context context) { if (context == null) { throw new IllegalArgumentException("Context must not be null."); } this.context = context.getApplicationContext();}private Downloader downloader;private ExecutorService service;private Cache cache;private Listener listener;private RequestTransformer transformer;private List<RequestHandler> requestHandlers;private Bitmap.Config defaultBitmapConfig;/** Create the {@link Picasso} instance. */public Picasso build() { Context context = this.context; // 为这一系列变量进行默认初始化 // Downloader执行实际的下载业务,返回Response if (downloader == null) { downloader = Utils.createDefaultDownloader(context); } // 内存缓存,可以看到默认的是LruCache if (cache == null) { cache = new LruCache(context); } // 线程池,执行网络请求的地方 if (service == null) { service = new PicassoExecutorService(); } // request发送前进行处理 if (transformer == null) { transformer = RequestTransformer.IDENTITY; } // 用以统计 Stats stats = new Stats(cache); // 进行Request以及Response的转发 Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats); // 创建一个Picasso return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats, defaultBitmapConfig, indicatorsEnabled, loggingEnabled);}
可以看到这里为许多重要类进行了默认初始化,后面获取图片使用到的都是这些默认的变量,当然这些变量也都可以进行自定义;
Downloader:默认实现是OkHttpDownloader,Picasso是对OkHttp的封装,它的网络请求以及对本地Cache的请求都是直接通过OkHttp来实现的;Downloader是实现本地磁盘查询,以及发起网络请求的类;
Cache:内存缓存;这里采用的LruCache,也可以自行定义。缓存的是进行网络请求后,大小等裁剪之后的图片资源。
PicassoExecutorService: Picasso之中的线程池,默认核心线程为3个,没有非核心线程;但也会根据手机当前的网络状态进行适时改变,比如WIFI状态的核心线程就为4个,而4G,3G,2G状态下的线程数分别为3,2,1个;
Stats:进行一些数据统计,比如图片下载命中率等。
RequestTransformer:用来预处理Request,必须修改域名等
Dispatcher:用以分发任务,它通过启动了一个DispatcherThread线程,然后创建一个用来处理消息的DispatcherHandler,该Handler的数据处理是在DispatcherThread中进行的。
RequestHandler: 后面将会看到,该对象用来持有一个具体的Request,所有Request最终其实都是由其对应的RequestHandler来进行处理的。
根据后面具体使用情况再做分析:
2、Picasso#load:
public RequestCreator load(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(Uri uri) { return new RequestCreator(this, uri, 0);}
这里创建一个RequestCreator
private final Picasso picasso;private final Request.Builder data;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);}
data对应创建一个Request的Builder,这个Request中封装了了相应的请求信息,传入了请求加载图片的URI,已经resourceId,以及默认显示图片的配置信息。
进而一般调用into将图片加载到相应的控件中。
3、RequestCreator#into:
// RequestCreator.javapublic void into(ImageView target) { into(target, null);}public void into(ImageView target, Callback callback) { long started = System.nanoTime(); // 盘算是否是在主线程,如果不是则会抛出异常 checkMain(); if (target == null) { throw new IllegalArgumentException("Target must not be null."); } // 判断reqeust是否合法,即存在URI或者对应的resId,否则会取消该请求 if (!data.hasImage()) { picasso.cancelRequest(target); if (setPlaceholder) { setPlaceholder(target, getPlaceholderDrawable()); } return; } // 判断是否需要延期执行 if (deferred) { // 判断是否已经设置了宽高大小 if (data.hasSize()) { throw new IllegalStateException("Fit cannot be used with resize."); } // 获取目标控件的宽高参数 int width = target.getWidth(); int height = target.getHeight(); // 表示当前控件并未加载到界面上(宽或高为0) if (width == 0 || height == 0) { if (setPlaceholder) { setPlaceholder(target, getPlaceholderDrawable()); } // 生成DeferredRequestCreator,加入相应队列进行处理 picasso.defer(target, new DeferredRequestCreator(this, target, callback)); return; } // 设置Request.Builder中的宽高参数大小 data.resize(width, height); } // 创建Request Request request = createRequest(started); // 获取request对应的key String requestKey = createKey(request); // 根据策略判断是够需要跳过读取MemoryCache if (shouldReadFromMemoryCache(memoryPolicy)) { // 尝试从MemoryCache中获取Bitmap // 根据Requeskey来获取相应的Bitmap Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey); if (bitmap != null) { // 如果从MemoryCache中获取到相应的bitmap,则取消request picasso.cancelRequest(target); // 设置图片 setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled); if (picasso.loggingEnabled) { log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY); } // 调用回调callback的onSuccess函数 if (callback != null) { callback.onSuccess(); } return; } } // 如果从MemoryCache获取图片失败,或者根据缓存策略直接跳过读取MemoryCache,则设置默认图片 if (setPlaceholder) { setPlaceholder(target, getPlaceholderDrawable()); } // 创建ImageViewAction,Action里面包含了一次请求所需要的所有信息 Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade); // 将action入队列 picasso.enqueueAndSubmit(action);}
整个图片加载流程和一般的三级缓存加载流程相似,下图已经介绍地很清晰:
上面主要是尝试从MemoryCache中获取对应Bitmap,如果获取失败,再创建Action,通过将Action入队列,然后从本地Cache以及网络上去获取该图片。
接着上面enqueueAndSubmit继续分析
4、Picasso#enqueueAndSubmit:
// Picasso.javafinal Map<Object, Action> targetToAction;void enqueueAndSubmit(Action action) { // 根据Action获取对应的Target Object target = action.getTarget(); if (target != null && targetToAction.get(target) != action) { // This will also check we are on the main thread. // 取消该target对应的以前的请求 cancelExistingRequest(target); // 添加当前请求 targetToAction.put(target, action); } // 提交action submit(action);}
targetToAction是一个Map,它存储的是Target对应的Action,以Target为相应key值。
当新添加一个action时,首先尝试取消该Action对应的Target之前的Action请求,然后将新Action添加到Map中;
然后提交action请求;
先来看下取消一个Request的函数cancelExistingReqeust,常用的cancelReqeust也是直接调用该函数实现的:
// Picasso.javafinal Map<Object, Action> targetToAction;private void cancelExistingRequest(Object target) { // 判断是否在主线程中进行 checkMain(); // 从Map中移除该target对应action Action action = targetToAction.remove(target); // 如果该Target之前存在对应的action if (action != null) { // 则调用该action的cancel函数进行取消 action.cancel(); // 并且调用Dispatcher进行分发cancel请求 dispatcher.dispatchCancel(action); } // 对于ImageView类型的Target if (target instanceof ImageView) { ImageView targetImageView = (ImageView) target; // 前面分析提到可能有需要延迟操作的请求,它是通过添加到DeferredRequestCreator来实现的,这里的需要将其cancel掉 DeferredRequestCreator deferredRequestCreator = targetToDeferredRequestCreator.remove(targetImageView); if (deferredRequestCreator != null) { deferredRequestCreator.cancel(); } }}
简单的cancel逻辑:
这里依然也需要先判断事件处理是否是在主线程中进行的;
然后尝试从targetToAction中删除该Target中对应的Action,如果该Action确实存在,则调用cancel函数取消该Action,并且调用Dispatcher来分发该cancel请求;
对应ImageView类型的target,前面分析中提到当需要defer即延迟的请求时,是通过创建DeferredReqeustCreator,并添加到一个Map中来实现的,这里同理应将其删除并cancel掉;
再来看看其cancel函数:
1)Action#cancel:
// ImageViewAction.javaclass ImageViewAction extends Action<ImageView> { Callback callback; @Override void cancel() { super.cancel(); if (callback != null) { callback = null; } }}// Action.javaabstract class Action<T> { boolean cancelled; void cancel() { cancelled = true; }}
和Volley这些差不多,通过设置一个cancel标志来实现。
2) Dispatcher#dispatchCacnel
// Dispatcher.javavoid dispatchCancel(Action action) { handler.sendMessage(handler.obtainMessage(REQUEST_CANCEL, action));}
分发逻辑较为简单,使用Handler来实现的;
这里来具体来看下Dispatcher的定义,来确定handler的定义;Dispathcer是在创建Picasso时默认创建了一个Dispathcer实例,如下所示:
// Picasso.javaDispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);static final Handler HANDLER = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { ... } }}
而Dispatcher中的Handler并不是这个Handler,而是又做了一个封装:
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
// Dispatcher.javaprivate static class DispatcherHandler extends Handler { private final Dispatcher dispatcher; public DispatcherHandler(Looper looper, Dispatcher dispatcher) { super(looper); this.dispatcher = dispatcher; } @Override public void handleMessage(final Message msg) { switch (msg.what) { case REQUEST_SUBMIT: { Action action = (Action) msg.obj; dispatcher.performSubmit(action); break; } case REQUEST_CANCEL: { Action action = (Action) msg.obj; dispatcher.performCancel(action); break; } ....... default: Picasso.HANDLER.post(new Runnable() { @Override public void run() { throw new AssertionError("Unknown handler message received: " + msg.what); } }); } }}
Dispathcer的作用是通过handler将相应的操作逻辑转换到自己Looper对应线程中去实现,然后调用performCancel去具体处理该Action;
下面接着来看Dispatcher#performCancel:
// Dispatcher.javafinal Map<String, BitmapHunter> hunterMap;final Set<Object> pausedTags;final Map<Object, Action> pausedActions;final Map<Object, Action> failedActions;void performCancel(Action action) { // 获取Action对应的key String key = action.getKey(); // 根据key获取对应的BitmapHunter BitmapHunter hunter = hunterMap.get(key); // 取消执行bitmapHunter if (hunter != null) { hunter.detach(action); if (hunter.cancel()) { hunterMap.remove(key); } } // 从暂停map中删除对应action if (pausedTags.contains(action.getTag())) { pausedActions.remove(action.getTarget()); } // 从失败map中删除对应Action Action remove = failedActions.remove(action.getTarget());}
逻辑较为简单,主要从各个可能存储该Action的集合中将其删除,如果已经有Bitmap在执行,则调用去cancel去处理。里面引出一个重要的类BitmapHunter,下面将会进行介绍。
如果没有从MemoryCache中获取到Bitmap(分两种情况,一直是根据policy直接跳过从MemoryCache中获取;另一种是MemoryCache中并没有对应的Bitmap缓存),这是将会来到submit(Action);
5、Picasso#submit:
// Picasso.javavoid submit(Action action) { dispatcher.dispatchSubmit(action);}
和前面类似。继续调用Dispatcher来分发submit事件,因此可以看出Dispatcher的主要作用是对Picasso(主线程)中发送的事件进行分发处理;
和dispatchCancel类似,接下来会来到performSubmit:
6、Dispatcher#performSubmit:
final Set<Object> pausedTags;final Map<Object, Action> failedActions;final Map<Object, Action> pausedActions;final Map<String, BitmapHunter> hunterMap;final ExecutorService service;void performSubmit(Action action, boolean dismissFailed) { // 如果Action已经暂停 if (pausedTags.contains(action.getTag())) { pausedActions.put(action.getTarget(), action); return; } // 获取对应的BitmapHunter BitmapHunter hunter = hunterMap.get(action.getKey()); if (hunter != null) { // 如果对应hunter不为null,表示前面有同样的请求在进行,因此这是可以合并请求 hunter.attach(action); return; } // 判断是否已经关闭 if (service.isShutdown()) { return; } // 创建BitmapHunter hunter = forRequest(action.getPicasso(), this, cache, stats, action); // 将Bitmap提交到线程池 hunter.future = service.submit(hunter); // 保存Bitmap hunterMap.put(action.getKey(), hunter); if (dismissFailed) { failedActions.remove(action.getTarget()); }}
具体的工作逻辑如下图:
当遇到相同的Action时,Picasso会通过BitmapHunter的Attach来合并相同的请求,避免同一个请求重复进行;简单看下attach:
1)Bitmap#attach:
// BitmapHunter.javavoid attach(Action action) { boolean loggingEnabled = picasso.loggingEnabled; Request request = action.request; // 如果BitmapHunter对应的action仍未设置,则直接设置action返回 if (this.action == null) { this.action = action; return; } // 创建一个保存相同Action的ArrayList if (actions == null) { actions = new ArrayList<Action>(3); } actions.add(action); // 获得Action的优先级 Picasso.Priority actionPriority = action.getPriority(); // 如果新添加Action的Priority优先级高于原来的优先级,则更新该Bitmaphunter的优先级 if (actionPriority.ordinal() > priority.ordinal()) { priority = actionPriority; }}
可以看到合并相同请求,则是在BitmapHunter中维护一个Action列表actions;
当新添加Action的Priority优先级高于原来的优先级,则更新该BitmapHunter的优先级大小;
这里在来关注下Picasso中的优先级问题:
/** * The priority of a request. * * @see RequestCreator#priority(Priority) */public enum Priority { LOW, NORMAL, HIGH}
Picasso中Priority是个枚举类,总共分为LOW,NORMAL,HIGH三个等级;而相对应地,Volley中Request的Priority则多了一个IMMEDIATE等级;顺便提下Volley中Reqeust可以根据Priority进行执行的原因是因为其实现了Comparable接口:
Volley#Reqeust:
public abstract class Request<T> implements Comparable<Request<T>> { public enum Priority { LOW, NORMAL, HIGH, IMMEDIATE } @Override public int compareTo(Request<T> other) { Priority left = this.getPriority(); Priority right = other.getPriority(); // High-priority requests are "lesser" so they are sorted to the front. // Equal priorities are sorted by sequence number to provide FIFO ordering. return left == right ? this.mSequence - other.mSequence : right.ordinal() - left.ordinal(); }}
2)ExecutorService:
再来看下线程池ExecutorService,它是在Picasso创建之初Builder时就创建的,默认的实现为:
// Picasso.Builderprivate ExecutorService service;if (service == null) { service = new PicassoExecutorService();}
2.1)来看PicassoExecutorService:
// PicassoExecutorServiceclass PicassoExecutorService extends ThreadPoolExecutor { // 默认的线程池的线程数量 private static final int DEFAULT_THREAD_COUNT = 3; PicassoExecutorService() { super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS, new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory()); }}
Picasso提供的是个默认核心线程数为3个,没有非核心线程(最大的线程数目也是为3),任务队列为PriorityBlockingQueue的线程池;
Picasso有个有趣的地方是,它可以根据当前应用的网络状态实时调整线程池中的线程数目:
2.2)PicassoExecutorService#adjustThreadCount:
// 根据网络状况调整线程池中线程的数量void adjustThreadCount(NetworkInfo info) { if (info == null || !info.isConnectedOrConnecting()) { setThreadCount(DEFAULT_THREAD_COUNT); return; } switch (info.getType()) { case ConnectivityManager.TYPE_WIFI: case ConnectivityManager.TYPE_WIMAX: case ConnectivityManager.TYPE_ETHERNET: setThreadCount(4); break; case ConnectivityManager.TYPE_MOBILE: switch (info.getSubtype()) { case TelephonyManager.NETWORK_TYPE_LTE: // 4G case TelephonyManager.NETWORK_TYPE_HSPAP: case TelephonyManager.NETWORK_TYPE_EHRPD: setThreadCount(3); break; case TelephonyManager.NETWORK_TYPE_UMTS: // 3G case TelephonyManager.NETWORK_TYPE_CDMA: case TelephonyManager.NETWORK_TYPE_EVDO_0: case TelephonyManager.NETWORK_TYPE_EVDO_A: case TelephonyManager.NETWORK_TYPE_EVDO_B: setThreadCount(2); break; case TelephonyManager.NETWORK_TYPE_GPRS: // 2G case TelephonyManager.NETWORK_TYPE_EDGE: setThreadCount(1); break; default: setThreadCount(DEFAULT_THREAD_COUNT); } break; default: setThreadCount(DEFAULT_THREAD_COUNT); }}// 线程线程数量private void setThreadCount(int threadCount) { setCorePoolSize(threadCount); setMaximumPoolSize(threadCount);}
即WIFI状态下线程数目为4,4G状态下为3,3G状态下为2,2G转态下线程池中只有一个线程;
再来看线程池中创建线程的Factory,看看Picasso的线程池中的线程有何特别之处:
2.3)PicassoThreadFactory:
static class PicassoThreadFactory implements ThreadFactory { @SuppressWarnings("NullableProblems") public Thread newThread(Runnable r) { return new PicassoThread(r); }}private static class PicassoThread extends Thread { public PicassoThread(Runnable r) { super(r); } @Override public void run() { Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND); super.run(); }}
PicassoThread简单的Thread,线程优先级为后台线程;
再看service提交submit一个BitmapHunter时的操作逻辑:
2.4)PicassoExecutorService#submit:
// PicassoExecutorServiceclass PicassoExecutorService extends ThreadPoolExecutor { // 提交任务 @Override public Future<?> submit(Runnable task) { PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task); execute(ftask); return ftask; } // 将BitmapHunter封装成PicassoFutureTask进行execute private static final class PicassoFutureTask extends FutureTask<BitmapHunter> implements Comparable<PicassoFutureTask> { private final BitmapHunter hunter; public PicassoFutureTask(BitmapHunter hunter) { super(hunter, null); this.hunter = hunter; } @Override public int compareTo(PicassoFutureTask other) { Picasso.Priority p1 = hunter.getPriority(); Picasso.Priority p2 = other.hunter.getPriority(); // High-priority requests are "lesser" so they are sorted to the front. // Equal priorities are sorted by sequence number to provide FIFO ordering. return (p1 == p2 ? hunter.sequence - other.hunter.sequence : p2.ordinal() - p1.ordinal()); } }}
可以看到在submit中将BitmapHunter封装成一个FutureTask进行execute;
PicassoFutureTask可以实现对当前Runnbale运行结果的查看及控制;同期它提供了和Volley一个样的Priority一样的优先级机制。
回到前面performSubmit的执行逻辑,接下来会创建以及执行BitmapHunter,下面重点介绍BitmapHunter,它是一个任务具体执行的整体:
7、BitmapHunter:
1)Dispatcher#forReqeust:
// BitmapHunter.javastatic BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) { // 根据Action获取对应Request Request request = action.getRequest(); // 获取requestHandlers List<RequestHandler> requestHandlers = picasso.getRequestHandlers(); // Index-based loop to avoid allocating an iterator. //noinspection ForLoopReplaceableByForEach // 查找使用能够处理该Request的requesthandler,如果有则传入BitmapHandler for (int i = 0, count = requestHandlers.size(); i < count; i++) { RequestHandler requestHandler = requestHandlers.get(i); if (requestHandler.canHandleRequest(request)) { return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler); } } // 使用默认的ERRORING_HANDLER来构造BitmapHunter return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);}
先判断Picasso中是否已经设置了能够处理该Request的requestHandler,如果存在则传入BitmapHandler,如果不存在,则使用默认的ERRORING_HANDLER来构造BitmapHandler;
简单看下ERRORING_HANDLER:
// BitmapHunter.javaprivate static final RequestHandler ERRORING_HANDLER = new RequestHandler() { @Override public boolean canHandleRequest(Request data) { return true; } @Override public Result load(Request request, int networkPolicy) throws IOException { throw new IllegalStateException("Unrecognized type of request: " + request); }};
事实上处理Request请求主要是通过reqeustHandler的load函数来进行处理的,然后将处理后的结果封装成Result,然后返回;Picasso在其构造函数中提供了一些列的RequestHandler:
List<RequestHandler> allRequestHandlers = new ArrayList<RequestHandler>(builtInHandlers + extraCount);// ResourceRequestHandler needs to be the first in the list to avoid// forcing other RequestHandlers to perform null checks on request.uri// to cover the (request.resourceId != 0) case.allRequestHandlers.add(new ResourceRequestHandler(context));if (extraRequestHandlers != null) { allRequestHandlers.addAll(extraRequestHandlers);}allRequestHandlers.add(new ContactsPhotoRequestHandler(context));allRequestHandlers.add(new MediaStoreRequestHandler(context));allRequestHandlers.add(new ContentStreamRequestHandler(context));allRequestHandlers.add(new AssetRequestHandler(context));allRequestHandlers.add(new FileRequestHandler(context));allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));requestHandlers = Collections.unmodifiableList(allRequestHandlers);
可以看到,按照不同的Request,Picasso提供了不同的RequestHandler进行处理,比如从资源中进行加载的则使用ResourceRequestHandler,从文件中加载则调用FileRequestHandler,从网络中直接下载使用NetworkRequestHandler等等。
下面将会看到这些RequestHandler的具体处理作用;
2)BitmapHunter:
// BitmapHunter.javaclass BitmapHunter implements Runnable { /** * Global lock for bitmap decoding to ensure that we are only are decoding one at a time. Since * this will only ever happen in background threads we help avoid excessive memory thrashing as * well as potential OOMs. Shamelessly stolen from Volley. */ private static final Object DECODE_LOCK = new Object(); @Override public void run() { try { updateThreadName(data); // 主要的执行逻辑在hunt方法中 result = hunt(); // 通过Dispatcher来分发运行之后的结果 if (result == null) { dispatcher.dispatchFailed(this); } else { 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); } }}
BitmapHunter实际上是一个Runnable,重点看其run中的执行逻辑;看到执行较为简单,都是通过hunt这个函数来实现的,获取到结果后,根据结果的成功与否,通过Dispatcher进行转发结果;
BitmapHunter中有一个成员变量较为有趣,DECODE_LOCK,这个变量在Volley中出现过,而且代码作者也说这是它从Volley中学习过来来,从名称中就可以看出,它是用来在解码图片时使用的锁,用来保证系统在同一时刻只有一个线程在解码图片;
2.1)Bitmap#hunt:
// BitmapHunter.javafinal Request data;Bitmap hunt() throws IOException { Bitmap bitmap = null; // 这里还是先尝试从MemoryCache中去获取Bitmap if (shouldReadFromMemoryCache(memoryPolicy)) { bitmap = cache.get(key); if (bitmap != null) { // 如果获取成功,直接返回 stats.dispatchCacheHit(); // 统计命中信息 loadedFrom = MEMORY; // loadedFrom记录表示从Memory缓存中获取 return bitmap; } } data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy; // 通过RequestHandler.load来处理Request获取结果 RequestHandler.Result result = requestHandler.load(data, networkPolicy); // 如果处理结果成功 if (result != null) { loadedFrom = result.getLoadedFrom(); exifRotation = result.getExifOrientation(); // 从结果中获取Bitmap bitmap = result.getBitmap(); // If there was no Bitmap then we need to decode it from the stream. // 如果Bitmap为null,则需要将Stream转化成Bitmap if (bitmap == null) { InputStream is = result.getStream(); try { bitmap = decodeStream(is, data); // 将网络数据流decode成Bitmap } finally { Utils.closeQuietly(is); } } } if (bitmap != null) { // 更新统计信息 stats.dispatchBitmapDecoded(bitmap); // 判断是否需要进行Transformation,即进行转换 if (data.needsTransformation() || exifRotation != 0) { // 这里体现了前面提到的DECODE_LOCK的作用,它保证在同一时刻,只有一个线程在执行图形变换 synchronized (DECODE_LOCK) { if (data.needsMatrixTransform() || exifRotation != 0) { bitmap = transformResult(data, bitmap, exifRotation); } if (data.hasCustomTransformations()) { bitmap = applyCustomTransformations(data.transformations, bitmap); } } if (bitmap != null) { stats.dispatchBitmapTransformed(bitmap); } } } return bitmap;}
hunt获取Bitmap的流程也较为清晰,首先仍然进行一次尝试从MemoryCache中进行获取;
获取失败,通过requestHandler的load函数进行加载,加载返回的结果为Result;
然后判断获取到的结果是否已经是Bitmap,如果不是,则需要将其从Stream转化成Bitmap;
然后判断是否需要Transformation;使用Transformation可以实现对获取到的图片的转化,一般的使用方式为:
Picasso.with(this).load("").transform(new Transformation() { @Override public Bitmap transform(Bitmap source) { // 进行图片转换 return null; } @Override public String key() { return null; }}).into(mImageView);
使用transform方法中添加Transformation进行图片处理,相应的图片处理开源库picasso-transformations源码地址已经集成了一些常用的Transformation可以直接进行使用;
transform的主要作用是将新创建Transformation对象添加到Request的transformations这个ArrayList中;
因而判断Request是否需要进行transform的方法:
// Request.javaboolean needsTransformation() { return needsMatrixTransform() || hasCustomTransformations();}// 判断是否需要矩阵变换boolean needsMatrixTransform() { return hasSize() || rotationDegrees != 0;}// 即将Bitmap转化成ImageView适合的尺寸public boolean hasSize() { return targetWidth != 0 || targetHeight != 0;}// 判断用户是否添加了相应的Transformationboolean hasCustomTransformations() { return transformations != null;}
然后根据需要,对图片进行转换;
这里转换时使用到了前面所提到的DECODE_LOCK,它保证在同一时刻,只有一个线程在执行图形变换操作;
流程梳理清楚了,下面接着来看通过requestHandler接着获取Bitmap的过程,这里通过NetworkRequestHandler为例,即从网络中获取;
2.2)NetworkRequestHandler#load:
// NetWorkRequestHandler.javaclass NetworkRequestHandler extends RequestHandler { private final Downloader downloader; @Override public RequestHandler.Result load(Request request, int networkPolicy) throws IOException { // 可以看到真正处理网络请求的操作时通过downloader来实现的 Downloader.Response response = downloader.load(request.uri, request.networkPolicy); // 下面处理response结果 if (response == null) { return null; } // 判断数据是从磁盘还是网络总获取的 Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK; Bitmap bitmap = response.getBitmap(); if (bitmap != null) { // 如果可以直接获取到Bitmap,则将其封装成Result,返回 return new RequestHandler.Result(bitmap, loadedFrom); } // 否则获取其输入流InputStream InputStream is = response.getInputStream(); if (is == null) { return null; } // Sometimes response content length is zero when requests are being replayed. Haven't found // root cause to this but retrying the request seems safe to do so. // 处理无效的Content长度为0的情况 if (loadedFrom == DISK && response.getContentLength() == 0) { Utils.closeQuietly(is); // 抛出ContentLengthException,在Bitmap的run函数中会处理该异常,会调用retry进行重试 throw new ContentLengthException("Received response with 0 content-length header."); } if (loadedFrom == NETWORK && response.getContentLength() > 0) { stats.dispatchDownloadFinished(response.getContentLength()); } // 封装成Result,返回 return new RequestHandler.Result(is, loadedFrom); }}
这里可以看到,真正处理请求的其实是Downloader这个实例,处理后返回的结果为Response;
Response中返回的结果可能有两种情况,一种是可能已经获取到了bitmap,一种可能是InputStream,这里需要分别进行处理,封装成Result,返回给BitmapHunter;
这里有个细节,当从磁盘中获取的结果Content的长度为0时,这里会抛出ContentLengthException异常,在BitmapHunter的run函数中会处理该异常,会调用retry进行重试;
downloader也是在Picasso初始化时就创建了的,如下所示:
if (downloader == null) { downloader = Utils.createDefaultDownloader(context);}
2.3)Downloader:
// Utils.javastatic Downloader createDefaultDownloader(Context context) { try { Class.forName("com.squareup.okhttp.OkHttpClient"); return OkHttpLoaderCreator.create(context); } catch (ClassNotFoundException ignored) { } return new UrlConnectionDownloader(context);}
可以看到这里提供了两种实现,一种是如果系统中已经添加了OkhttpClient依赖,则使用Class.forName加载OkHttpClient类时,会加载成功,这时使用OkHttpLoaderCreator进行创建,也就是下面的网络请求通过okHttp来实现;否则是使用Android中默认提供的UrlConnection;这里一句OkHttp来进行分析;
// Utils.javaprivate static class OkHttpLoaderCreator { static Downloader create(Context context) { return new OkHttpDownloader(context); }}
则Downloader的真是类型为OkHttpDownloader;
先来看下OkHttpDownloader的一些具体细节:
// Utils.java// 创建Cache的文件地址private static final String PICASSO_CACHE = "picasso-cache";static File createDefaultCacheDir(Context context) { File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE); if (!cache.exists()) { //noinspection ResultOfMethodCallIgnored cache.mkdirs(); } return cache;}// OkHttpDownloader.javapublic class OkHttpDownloader implements Downloader { private static OkHttpClient defaultOkHttpClient() { OkHttpClient client = new OkHttpClient(); // 设置OkHttpClient中的一些参数 client.setConnectTimeout(Utils.DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); client.setReadTimeout(Utils.DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); client.setWriteTimeout(Utils.DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); return client; } private final OkHttpClient client; /** * Create new downloader that uses OkHttp. This will install an image cache into your application * cache directory. */ // 一系列构造函数 // 构造函数的主要作用其实只是为了创建OkHttpClient public OkHttpDownloader(final Context context) { this(Utils.createDefaultCacheDir(context)); } public OkHttpDownloader(final File cacheDir) { this(cacheDir, Utils.calculateDiskCacheSize(cacheDir)); } public OkHttpDownloader(final File cacheDir, final long maxSize) { this(defaultOkHttpClient()); try { // 设置Cache地址,使用的是OkHttp中的Cache client.setCache(new com.squareup.okhttp.Cache(cacheDir, maxSize)); } catch (IOException ignored) { } } public OkHttpDownloader(OkHttpClient client) { this.client = client; }}
这里提供了一系列OkHttpDownloader的构造函数及创建细节,其实最主要的工作只是为了创建OkHttpClient;
具体来看其load执行网络请求的细节:
// OkHttpDownloader.java@Overridepublic Downloader.Response load(Uri uri, int networkPolicy) throws IOException { CacheControl cacheControl = null; // 构建Cache的相关执行策略 if (networkPolicy != 0) { if (NetworkPolicy.isOfflineOnly(networkPolicy)) { // 如果要求强制从网络中获取 cacheControl = CacheControl.FORCE_CACHE; } else { CacheControl.Builder builder = new CacheControl.Builder(); if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) { // 判断是否允许读取Disk缓存 builder.noCache(); // DiskCache不允许读 } if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) { // 判断是否允许写入Disk缓存 builder.noStore(); // DiskCache不允许写 } cacheControl = builder.build(); } } // 根据URI创建OkHttp中的Request com.squareup.okhttp.Request.Builder builder = new com.squareup.okhttp.Request.Builder().url(uri.toString()); if (cacheControl != null) { builder.cacheControl(cacheControl); } // 典型的okHttp的同步执行方式来获取网络请求 com.squareup.okhttp.Response response = client.newCall(builder.build()).execute(); int responseCode = response.code(); // 处理失败的请求 if (responseCode >= 300) { response.body().close(); throw new Downloader.ResponseException(responseCode + " " + response.message(), networkPolicy, responseCode); } boolean fromCache = response.cacheResponse() != null; ResponseBody responseBody = response.body(); // 将获取到的结果封装成Response返回给RequestHandler return new Downloader.Response(responseBody.byteStream(), fromCache, responseBody.contentLength());}
请求过程完全交由OkHttp来实现的,过程较为简单,不再赘述;这里有个细节问题,这里使用的OkHttp中的同步执行方式,因为这里执行load的线程就已经是非主线程了,故选择同步执行方式即可;具体的从DISK以及Network中来获取Response的工作完全交给OkHttp进行执行即可;
至此获取Bitmap的工作已经完成,主要的所有逻辑可以总结为
1)首先创建一个Request,Request中包含了一个请求中的相关信息,这是尝试从MemoryCache中获取,如果获取成功则直接返回,否则将Request封装成Action,然后enqueueAndSubmit;
2)在submit中,如果有相同的Action请求,则合并后来的Action请求,同一个actions集合,只需要执行一个Action就可以获取到结果;
如果之前并没有相同的Action请求,则使用Action创建一个Runnable即重要的BitmapHunter,通过线程池ExecutorService来submit执行;
3)BitmapHunter的主要执行逻辑在run函数中;而获取Bitmap的操作主要通过hunt函数来获取;hunt接着调用Request相对应的适合处理该request的RequestHandler的load函数进行处理,比如处理网络请求则需要使用NetworkRequestHandler;
4)RequestHandler的load函数其实也只是一层封装,真正执行网络请求的是Downloader这个类,对应于实际情况就是OkHttpDownloader;它的所有网络请求也都是直接交由OkHttp来完成的,因此Picasso和Retrofit一样都可以看做是对OkHttp的进一步封装;
获取Bitmap成功后,回来BitmapHunter的run方法继续来看对于结果是怎么处理的:
// 通过Dispatcher来分发运行之后的结果if (result == null) { dispatcher.dispatchFailed(this);} else { dispatcher.dispatchComplete(this);}
2.4)Dispatcher:
// Dispatchervoid dispatchComplete(BitmapHunter hunter) { handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));}private static class DispatcherHandler extends Handler { @Override public void handleMessage(final Message msg) { switch (msg.what) { case HUNTER_COMPLETE: { BitmapHunter hunter = (BitmapHunter) msg.obj; dispatcher.performComplete(hunter); break; } } }}void performComplete(BitmapHunter hunter) { // 判断是否允许写入到MemoryCache中 if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) { cache.set(hunter.getKey(), hunter.getResult()); } // 从hunterMap中移除该BitmapHunter hunterMap.remove(hunter.getKey()); batch(hunter);}final List<BitmapHunter> batch;private static final int BATCH_DELAY = 200; // msprivate void batch(BitmapHunter hunter) { // 如果hunter取消了,则直接返回 if (hunter.isCancelled()) { return; } // 进行打包发送 batch.add(hunter); if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) { handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY); }}
Dispatcher即进行一些结果上的转发以及线程间的转换,都是同handler来实现的;前面提到这里handler对应的是DispatcherHandler,对应的Dispatcher所在的线程,然后使用该handler分发结果;
首先判断系统是否允许将获取到的结果写入到memoryCache中;然后执行完成过一个BitmapHunter之后,注意将其从hunterMap中移除;Picasso中分发结果是采用batch打包分发的,它每200ms分发一次,通过的就是batch函数中实现的;
handler发送一个延迟时间为200ms的类型为 HUNTER_DELAY_NEXT_BATCH的Message,在这200ms之内,下面将要转发的hunter事件,因为handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)为true,故而会添加到一个ArrayList batch中,等到延迟时间到了,这200ms内所到达的所有hunter都会存储到batch中一并进行发送;来看具体的发送函数:
// Dispatcherprivate static class DispatcherHandler extends Handler { @Override public void handleMessage(final Message msg) { switch (msg.what) { case HUNTER_DELAY_NEXT_BATCH: { dispatcher.performBatchComplete(); break; } } }}void performBatchComplete() { List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch); batch.clear(); mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy)); logBatch(copy);}
即通过主线程的Handler将结果发送到主线程;
mainThreadHandler对应的是Picasso中的静态内部类,来看其处理逻辑
//Picasso.javastatic final Handler HANDLER = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case HUNTER_BATCH_COMPLETE: { @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj; //noinspection ForLoopReplaceableByForEach for (int i = 0, n = batch.size(); i < n; i++) { BitmapHunter hunter = batch.get(i); hunter.picasso.complete(hunter); } break; } } }}
继续来看complete:
//Picasso.javavoid complete(BitmapHunter hunter) { // 获取Action 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 ForLoopReplaceableByForEach for (int i = 0, n = joined.size(); i < n; i++) { Action join = joined.get(i); deliverAction(result, from, join); } } // 设置Listener情况 if (listener != null && exception != null) { listener.onImageLoadFailed(this, uri, exception); }}
处理最终的结果,对于有合并相同Action的请求,则需要对应地分别进行处理;处理的函数均为deliverAction:
//Picasso.javaprivate 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."); } action.complete(result, from); } else { action.error(); }}
盗用一张图来解释所有的处理逻辑:
- Picasso源码解析
- Picasso源码解析 一
- Picasso源码解析
- Picasso源码解析
- Picasso源码解析
- Picasso源码解析
- picasso源码解析3
- picasso 源码解析
- Picasso源码解析
- Picasso源码解析
- Picasso源码解析
- picasso源码简单解析
- Picasso 用法及源码解析
- picasso图片框架源码解析
- Picasso源码完全解析(二)--Picasso实例的创建
- Picasso框架源码解析(一)。
- Picasso的使用和源码解析
- Picasso源码的简单解析(一)
- windows 无法找到unistd.h 的解决方法
- 理解LSTM网络
- 初识Volley框架 加载以及查看json数据+html元素
- Idea的全局搜索快捷键不起作用的解决方法
- 洛谷P1656 炸铁路
- Picasso源码解析
- IOS中使用像素位图(CGImageRef)对图片进行处理
- 在java中使用dom4j解析xml(示例代码)
- failed to push some refs to 'git@github.com:songmengke/NotePadVMware.git'
- spark sortByKey
- 简单 Java 加密算法 1.0
- 自动检测溢出的unsigned int加法
- 微信三方登录
- 设计模式笔记:工厂模式