简书:
前一篇文章讲了Picasso的详细用法,Picasso 是一个强大的图片加载缓存框架,一个非常优秀的开源库,学习一个优秀的开源库,,我们不仅仅是学习它的用法,停留在使用API层面,我们也要试着去阅读源码,有两个方面的原因,第一,熟悉了源码我们才能更好的驾驭,项目中做我们需要的定制。第二,学习它的设计思想、编码风格、代码的架构,然后在项目中对这些好的思想和架构加以实践,变成自己的知识,这样才会对我们有更多的提升和帮助。这也是我们学习的目的,因此这篇文章对Picasso 的源码和流程做一个分析。
一、Picasso 加载图片流程图
上面就是Picasso加载图片的流程,图画的丑,各位见谅。
二、重要的类介绍
(0)Picasso
: 图片加载、转换、缓存的管理类。单列模式 ,通过with
方法获取实例,也是加载图片的入口。
RequestCreator
: Request构建类,Builder 模式,采用链式设置该Request的属性(如占位图、缓存策略、裁剪规则、显示大小、优先级等等)。最后调用build()
方法生成一个请求(Request)。(2)DeferredRequestCreator:
RequestCreator的包装类,当创建请求的时候还不能获取ImageView的宽和高的时候,则创建一个DeferredRequestCreator,DeferredRequestCreator里对 target 设置监听,直到可以获取到宽和高的时候重新执行请求创建。 (3) Action
: 请求包装类,存储了该请求和RequestCreator设置的这些属性,最终提交给线程执行下载。(4)Dispatcher
:分发器,分发执行各种请求、分发结果等等。(5)PicassoExecutorService
:Picasso使用的线程池,默认池大小为3。(6)LruCache
:一个使用最近最少使用策略的内存缓存。(7)BitmapHunter
:这是Picasso的一个核心的类,开启线程执行下载,获取结果后解码成Bitmap,然后做一些转换操作如图片旋转、裁剪等,如果请求设置了转换器Transformation,也会在BitmapHunter里执行这些转换操作。(8)NetworkRequestHandler
:网络请求处理器,如果图片需要从网络下载,则用这个处理器处理。(9)FileRequestHandler
:文件请求处理器,如果请求的是一张存在文件中的图片,则用这个处理器处理。(10)AssetRequestHandler
: Asset 资源图片处理器,如果是加载asset目录下的图片,则用这个处理器处理。(11)ResourceRequestHandler
:Resource资源图片处理器,如果是加载res下的图片,则用这个处理器处理。(12)ContentStreamRequestHandler
: ContentProvider 处理器,如果是ContentProvider提供的图片,则用这个处理器处理(13)MediaStoreRequestHandler
: MediaStore 请求处理器,如果图片是存在MediaStore上的则用这个处理器处理。(14)ContactsPhotoRequestHandler
:ContactsPhoto 请求处理器,如果加载com.android.contacts/ 下的tu图片用这个处理器处理。如: // e.g. content://com.android.contacts/contacts/38)//匹配的路径如下:static { matcher = new UriMatcher(UriMatcher.NO_MATCH); matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", ID_LOOKUP); matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", ID_LOOKUP); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", ID_THUMBNAIL); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", ID_CONTACT); matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", ID_DISPLAY_PHOTO); }复制代码
上面8-14 是默认的提供的几个处理器,分别处理不同来源的请求。
(15)Response
: 返回的结果信息,Stream流或者Bitmap。
Request
: 请求实体类,存储了应用在图片上的信息。(17)Target
:图片加载的监听器接口,有3个回调方法,onPrepareLoad 在请求提交前回调,onBitmapLoaded 请求成功回调,并返回Bitmap,onBitmapFailed请求失败回调。(18)PicassoDrawable
:继承BitmapDrawable,实现了过渡动画和图片来源的标识(就是图片来源的指示器,要调用 setIndicatorsEnabled(true)
方法才生效),请求成功后都会包装成BitmapDrawable显示到ImageView 上。(19)OkHttpDownloader
:用OkHttp实现的图片下载器,默认就是用的这个下载器。(20)UrlConnectionDownloader
:使用HttpURLConnection 实现的下载器。(21)MemoryPolicy
: 内存缓存策略,一个枚举类型。(22)NetworkPolicy
: 磁盘缓存策略,一个枚举类型。 (23) Stats
: 这个类相当于日志记录,会记录如:内存缓存的命中次数,丢失次数,下载次数,转换次数等等,我们可以通过StatsSnapshot
类将日志打印出来,看一下整个项目的图片加载情况。(24)StatsSnapshot
:状态快照,和上面的Stats
对应,打印Stats
纪录的信息。 以上就是Picasso 的一些关键的类的介绍(还有一些简单的没有列举)。
三、流程分析
上一节介绍了Picasso 的一些关键类,接下来就以加载网络图片为例,分析Picasso加载图片的整个流程
Picasso.with(this).load(URL) .placeholder(R.drawable.default_bg) .error(R.drawable.error_iamge) .into(mBlurImage);复制代码
1, 获取Picasso instance
首先要获取一个Picasso对象,采用的单例模式
//单例模式获取Picasso 对象public static Picasso with(Context context) { if (singleton == null) { synchronized (Picasso.class) { if (singleton == null) { singleton = new Builder(context).build(); } } } return singleton; }// 真正new 的地方在build()方法里 public Picasso build() { Context context = this.context; if (downloader == null) { //配置默认的下载器,首先通过反射获取OkhttpClient,如果获取到了,就使用OkHttpDwownloader作为默认下载器 //如果获取不到就使用UrlConnectionDownloader作为默认下载器 downloader = Utils.createDefaultDownloader(context); } if (cache == null) { // 配置内存缓存,大小为手机内存的15% cache = new LruCache(context); } if (service == null) { // 配置Picaso 线程池,核心池大小为3 service = new PicassoExecutorService(); } if (transformer == null) { // 配置请求转换器,默认的请求转换器没有做任何事,直接返回原请求 transformer = RequestTransformer.IDENTITY; } Stats stats = new Stats(cache); //分发器 Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats); return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats, defaultBitmapConfig, indicatorsEnabled, loggingEnabled); }复制代码
2, 通过load方法生成一个RequestCreator
通过load方法生成一个RequestCreator,用链式api 来构建一个图片下载请求
//load有几个重载方法,参数为string和File 的重载最重都会包装成一个Uri 调用这个方法 public RequestCreator load(Uri uri) { return new RequestCreator(this, uri, 0); }// 如果是加载资源id 的图片会调用这个方法 public RequestCreator load(int resourceId) { if (resourceId == 0) { throw new IllegalArgumentException("Resource ID must not be zero."); } return new RequestCreator(this, null, resourceId); }复制代码
RequestCreator提供了很多的API 来构建请求,如展位图、大小、转换器、裁剪等等,这些API其实是为对应的属性赋值,最终会在into方法中构建请求。
// 配置占位图,在加载图片的时候显示public RequestCreator placeholder(int placeholderResId) { if (!setPlaceholder) { throw new IllegalStateException("Already explicitly declared as no placeholder."); } if (placeholderResId == 0) { throw new IllegalArgumentException("Placeholder image resource invalid."); } if (placeholderDrawable != null) { throw new IllegalStateException("Placeholder image already set."); } this.placeholderResId = placeholderResId; return this; }// 配置真正显示的大小 public RequestCreator resize(int targetWidth, int targetHeight) { data.resize(targetWidth, targetHeight); return this; }复制代码
3,into 添加显示的View,并且提交下载请求
into方法里面干了3件事情:
1, 判断是否设置了fit 属性,如果设置了,再看是否能够获取ImageView 的宽高,如果获取不到,生成一个DeferredRequestCreator(延迟的请求管理器),然后直接return,在DeferredRequestCreator中当监听到可以获取ImageView 的宽高的时候,再执行into方法。
2, 判断是否从内存缓存获取图片,如果没有设置NO_CACHE,则从内存获取,命中直接回调CallBack 并且显示图片。
3, 如果缓存未命中,则生成一个Action,并提交Action。
public void into(ImageView target, Callback callback) { long started = System.nanoTime(); // 检查是否在主线程 checkMain(); if (target == null) { throw new IllegalArgumentException("Target must not be null."); } //如果没有url或者resourceId 则取消请求 if (!data.hasImage()) { picasso.cancelRequest(target); if (setPlaceholder) { setPlaceholder(target, getPlaceholderDrawable()); } return; } //判断是否设置了fit属性 if (deferred) { if (data.hasSize()) { throw new IllegalStateException("Fit cannot be used with resize."); } int width = target.getWidth(); int height = target.getHeight(); if (width == 0 || height == 0) { if (setPlaceholder) { setPlaceholder(target, getPlaceholderDrawable()); } //如果获取不到宽高,生成一个DeferredRequestCreator(延迟的请求管理器),然后直接return, //在DeferredRequestCreator中当监听到可以获取ImageView 的宽高的时候,再执行into方法。 picasso.defer(target, new DeferredRequestCreator(this, target, callback)); return; } data.resize(width, height); } Request request = createRequest(started); String requestKey = createKey(request); //是否从内存缓存中获取 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 ,提交执行。 Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade); picasso.enqueueAndSubmit(action);// 提交请求 }复制代码
4, 提交、分发、执行请求。
会经过下面这一系列的操作,最重将Action 交给BitmapHunter 执行。
enqueueAndSubmit -> submit -> dispatchSubmit -> performSubmit://将action 保存到了一个Map 中,目标View作为key void enqueueAndSubmit(Action action) { Object target = action.getTarget(); 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); }// 交给分发器分发提交请求void submit(Action action) { dispatcher.dispatchSubmit(action); }//执行请求提交//1, 先查看保存暂停tag表里面没有包含Action的tag,如果包含,则将Action 存到暂停Action表里//2,从BitmapHunter表里查找有没有对应action的hunter,如果有直接attach//3, 为这个请求生成一个BitmapHunter,提交给线程池执行 void performSubmit(Action action, boolean dismissFailed) { // 先查看保存暂停tag表里面没有包含Action的tag,如果包含,则将Action 存到暂停Action表里 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 hunter = hunterMap.get(action.getKey()); if (hunter != null) { hunter.attach(action); return; } // 如果线程池北shutDown,直接return if (service.isShutdown()) { if (action.getPicasso().loggingEnabled) { log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down"); } return; } // 为请求生成一个BitmapHunter 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()); } }复制代码
5,指定对应的处理器(RequestHandler)
在上面执行的请求的performSubmit 方法里,调用了forRequest 方法为对应的Action 生成一个BitmapHunter,里面有一个重要的步骤,指定请求处理器(在上面一节介绍Picasso有7种请求处理器,看一下对应的代码:
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) { Request request = action.getRequest(); ListrequestHandlers = picasso.getRequestHandlers(); // Index-based loop to avoid allocating an iterator. //noinspection ForLoopReplaceableByForEach for (int i = 0, count = requestHandlers.size(); i < count; i++) { RequestHandler requestHandler = requestHandlers.get(i); // 循环请求处理器列表,如果找到有能处理这个请求的请求处理器 // 则生成BitmapHunter if (requestHandler.canHandleRequest(request)) { return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler); } } return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER); }复制代码
从Picasso里获取一个处理器列表,然后循环列表,看是否有能处理该请求的处理器,如果有,则生成BitmapHunter,那么这个请求处理器的列表在哪儿初始化的呢?请看源码:
// 1,首先调用了getRequestHandlers ListgetRequestHandlers() { return requestHandlers; }// 2 requestHandlers 列表是在Picasso 构造函数里出实话的 Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener, RequestTransformer requestTransformer, List extraRequestHandlers, Stats stats, Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) { .... //前面代码省略 // 添加了7个内置的请求处理器 // 如果你自己通过Builder添了额外的处理器,也会添加在这个列表里面 int builtInHandlers = 7; // Adjust this as internal handlers are added or removed. int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0); List allRequestHandlers = new ArrayList (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); //后面代码省略 ... }复制代码
小结: 在Picasso 的构造函数里 初始化了内置的7中请求处理器,然后在生成BitmapHunter的时候,循环列表,找到可以处理对应请求的处理器。
6, 重点:BitmapHunter (图片捕获器)
上一节重要类介绍的时候介绍过BitmapHunter,BitmapHunter继承Runnable,其实就是开启一个线程执行最终的下载。看一下源码:
1, run() 方法@Override public void run() { try { updateThreadName(data); if (picasso.loggingEnabled) { log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this)); } // 调用hunt() 方法获取最终结果 result = hunt(); if (result == null) { dispatcher.dispatchFailed(this);//如果为null,分发失败的消息 } else { dispatcher.dispatchComplete(this);//如果不为null,分发成功的消息 } } 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 submit 给一个线程池执行的时候,就会执行run() 方法,run里面调用的是hunt方法来获取结果,看一下hunt
方法:
Bitmap hunt() throws IOException { Bitmap bitmap = null; // 是否从内存缓存获取Bitmap 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; // 请求处理器处理请求,获取结果,Result里可能是Bitmap,可能是Stream 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) { stats.dispatchBitmapTransformed(bitmap); } } } return bitmap; }复制代码
7,Downloader 下载器下载图片
上面的hunt方法获取结果的时候,最终调用的是配置的处理器的load方法,如下:
RequestHandler.Result result = requestHandler.load(data, networkPolicy);复制代码
加载网络图片用的是NetworkRequestHandler,匹配处理器,有个canHandleRequest 方法:
@Override public boolean canHandleRequest(Request data) { String scheme = data.uri.getScheme(); return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme)); }复制代码
判断的条件是,Uri带有"http://" 或者 https:// 前缀则可以处理
我们接下来看一下NetworkRequestHandler的load方法:
@Override public Result load(Request request, int networkPolicy) throws IOException { //最终调用downloader的load方法获取结果 Response response = downloader.load(request.uri, request.networkPolicy); if (response == null) { return null; } Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK; Bitmap bitmap = response.getBitmap(); if (bitmap != null) { return new Result(bitmap, loadedFrom); } 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. if (loadedFrom == DISK && response.getContentLength() == 0) { Utils.closeQuietly(is); throw new ContentLengthException("Received response with 0 content-length header."); } if (loadedFrom == NETWORK && response.getContentLength() > 0) { stats.dispatchDownloadFinished(response.getContentLength()); } return new Result(is, loadedFrom); }复制代码
NetworkRequestHandler最终是调用的downloader 的load方法下载图片。内置了2个Downloader,OkhttpDownloader和UrlConnectionDownloader 。我们以UrlConnectionDownloader为例,来看一下load方法:
@Override public Response load(Uri uri, int networkPolicy) throws IOException { // 如果SDK 版本大于等于14,安装磁盘缓存,用的是HttpResponseCache(缓存http或者https的response到文件系统) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { installCacheIfNeeded(context); } HttpURLConnection connection = openConnection(uri); //设置使用缓存 connection.setUseCaches(true); if (networkPolicy != 0) { String headerValue; // 下面一段代码是设置缓存策略 if (NetworkPolicy.isOfflineOnly(networkPolicy)) { headerValue = FORCE_CACHE; } else { StringBuilder builder = CACHE_HEADER_BUILDER.get(); builder.setLength(0); if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) { builder.append("no-cache"); } if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) { if (builder.length() > 0) { builder.append(','); } builder.append("no-store"); } headerValue = builder.toString(); } connection.setRequestProperty("Cache-Control", headerValue); } int responseCode = connection.getResponseCode(); if (responseCode >= 300) { connection.disconnect(); throw new ResponseException(responseCode + " " + connection.getResponseMessage(), networkPolicy, responseCode); } long contentLength = connection.getHeaderFieldInt("Content-Length", -1); boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE)); // 最后获取InputStream流包装成Response返回 return new Response(connection.getInputStream(), fromCache, contentLength); }复制代码
小结:梳理一下调用链, BitmapHunter -> NetworkRequestHandler -> UrlConnectionDownloader(也有可能是OkHttpDownloader),经过这一系列的调用,最后在BitmapHunter 的run 方法中就可以获取到我们最终要的Bitmap。
8,返回结果并显示在Target上
在BitmapHunter获取结果后,分发器分发结果,通过Hander处理后,执行performComplete方法:
//1,void performComplete(BitmapHunter hunter) { // 这里将结果缓存到内存 if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) { cache.set(hunter.getKey(), hunter.getResult()); } hunterMap.remove(hunter.getKey());// 请求完毕,将hunter从表中移除 batch(hunter); if (hunter.getPicasso().loggingEnabled) { log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion"); } }// 2,然后将BitmapHunter添加到一个批处理列表,通过Hander发送一个批处理消息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); } }// 3,最后执行performBatchComplete 方法,通过主线程的Handler送处理完成的消息void performBatchComplete() { Listcopy = new ArrayList (batch); batch.clear(); mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy)); logBatch(copy); }// 4,最后在Picasso 中handleMessage,显示图片 static final Handler HANDLER = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case HUNTER_BATCH_COMPLETE: { @SuppressWarnings("unchecked") List batch = (List ) msg.obj; //noinspection ForLoopReplaceableByForEach for (int i = 0, n = batch.size(); i < n; i++) { BitmapHunter hunter = batch.get(i); hunter.picasso.complete(hunter); } break; } //后面代码省略 ... };// 5,最后回调到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 并显示 PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled); if (callback != null) { callback.onSuccess(); 回调callback } }复制代码
小结:通过上面一系列的方法调用, performComplete -> batch —> performBatchComplete -> handleMessage -> complete 把BitmapHunter中获取到的结果回调到主线程,并且显示在Target上。
通过以上的8个步骤,就把图片从加载到显示的整个过程分析完了。
四,缓存特别说明
内存缓存很简单,用的是LRUCache,大小为 手机内存的15% ,上面代码中已经分析过了,这里不过多说明,这里重点说一下Disk Cahce。Picasso内存了2个默认的下载器,UrlConnectionDownloader和OkHttpDownloader,它们的磁盘缓存实现还是有一些差异的,看一下代码:
public OkHttpDownloader(final File cacheDir, final long maxSize) { this(defaultOkHttpClient()); try { client.setCache(new com.squareup.okhttp.Cache(cacheDir, maxSize)); } catch (IOException ignored) { } }复制代码
在OkHttpDownloader 的构造方法里设置了磁盘缓存,使用的okHttp 的 DiskLruCache 实现的。
然后看一下UrlConnectionDownloader的磁盘缓存实现,代码:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { installCacheIfNeeded(context); }复制代码
private static void installCacheIfNeeded(Context context) { // DCL + volatile should be safe after Java 5. if (cache == null) { try { synchronized (lock) { if (cache == null) { cache = ResponseCacheIcs.install(context); } } } catch (IOException ignored) { } } } private static class ResponseCacheIcs { static Object install(Context context) throws IOException { File cacheDir = Utils.createDefaultCacheDir(context); HttpResponseCache cache = HttpResponseCache.getInstalled(); if (cache == null) { long maxSize = Utils.calculateDiskCacheSize(cacheDir); cache = HttpResponseCache.install(cacheDir, maxSize); } return cache; } static void close(Object cache) { try { ((HttpResponseCache) cache).close(); } catch (IOException ignored) { } } }复制代码
UrlConnectionDownloader 的磁盘缓存是用HttpResponseCache实现的
尽管2种磁盘缓存实现的方式不一样,但是它们的最后结果都是一样的:
1,磁盘缓存的地址: 磁盘缓存的地址在:data/data/your package name/cache/picasso-cache /
2,磁盘缓存的大小:磁盘缓存的大小为 手机磁盘大小的2% ,不超过50M不小于5M。3, 缓存的控制方式一样:都是在请求的header设置Cache-Control
的值来控制是否缓存。 缓存清除:
有同学在前一篇文章()下面留言问怎么清除缓存,这里统一说一下:1, 清除内存缓存:调用invalidate方法,如:Picasso.with(this) .invalidate("http://ww3.sinaimg.cn/large/610dc034jw1fasakfvqe1j20u00mhgn2.jpg");复制代码
清除指定url 的内存缓存。
但是Picasso没有提供清除全部内存缓存的方法,那就没有办法了吗?办法还是有的,LRUCahce 提供了clear方法的,只是Picasso没有向外部提供这个接口,因此可以通过反射获取到Picasso的cache字段,然后调用clear方法清除。
2, 清除磁盘缓存
很遗憾Picasso没有提供清除磁盘缓存的方法。它没有提供方法我们就自己想办法呗。思路:很简单,既然我们知道磁盘缓存是存在:data/data/your package name/cache/picasso-cache 这个路径下的,那我们把这个文件夹下面的所有文件清除不就行了。
实现:
private void clearDiskCache(){ File cache = new File(this.getApplicationContext().getCacheDir(), "picasso-cache"); deleteFileOrDirectory(cache.getPath()); } public static void deleteFileOrDirectory(String filePath){ if(TextUtils.isEmpty(filePath)){ return; } try { File file = new File(filePath); if(!file.exists()){ return; } if(file.isDirectory()){ File files[] = file.listFiles(); for(int i=0;i
好了,就用上面一段代码就可以实现删除磁盘缓存了。
最后
以上就是对Picasso的源码分析,代码中的关键部分也有添加注释,到此,Picasso的使用和源码分析就讲完了,还没有看前一篇文章()的可以去看一下,如有问题,欢迎留言交流。