1 功能介绍 Picasso是square推出的一个Android平台的图片加载开源库。 包含内存缓存和磁盘缓存两级缓存。 可定制度高、自定义配置、提供接口自定义处理图片等。 在 Adapter 中自动处理 ImageView 的缓存并且取消之前的图片下载任务。
2.总体设计
###3 详细设计
3.1 加载流程图及类图 流程图:
类图:
3.2 核心类分析 3.2.1 Picasso.Builder 这是一个通过Build创建Picasso的工具类。
3.2.2 Picasso.java Picasso保存了所有请求的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private final Listener listener;private final RequestTransformer requestTransformer;private final CleanupThread cleanupThread;private final List<RequestHandler> requestHandlers;final Context context;final Dispatcher dispatcher;final Cache cache;final Stats stats;final Map<Object, Action> targetToAction;final Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;final ReferenceQueue<Object> referenceQueue;final Bitmap.Config defaultBitmapConfig;Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener, RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats, Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) public static Picasso with (Context context) public RequestCreator load (...)
这里会检查当前ImageView是否已经存在加载任务,如果存在直接取消任务,开始新的任务。//这样防止了ListView中的ImageView复用问题。
1 2 3 4 5 6 7 8 9 void enqueueAndSubmit (Action action) {Object target = action.getTarget(); if (target != null && targetToAction.get(target) != action) { cancelExistingRequest(target); targetToAction.put(target, action); } submit(action); }
3.2.3 Downloader.java 图片下载客户端。默认为OkHttpDownloader,okhttp存在DiskLruCache数据缓存,如果使用其他的下载客户端(HttpURLConnection、HttpClient)需要自己定义数据本地缓存。
1 Response load (Uri uri, int networkPolicy) throws IOException ;
3.2.4 Cache.java 运行内存图片缓存。默认使用的缓存是LruCache,基本原理就是向缓存中添加图片,计算图片的大小,计算缓存所有图片的总大小,如果总图片的大小超过设定的大小,就删除使用频率低的图片。
1 2 3 4 5 Bitmap get (String key) ; void set (String key, Bitmap bitmap)
3.2.5 Dispatcher.java 请求分发和请求接口回调。通过命名可以清晰的知道其含义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void dispatchSubmit (Action action) void dispatchCancel (Action action) void dispatchPauseTag (Object tag) void dispatchResumeTag (Object tag) void dispatchComplete (BitmapHunter hunter) void dispatchRetry (BitmapHunter hunter) void dispatchFailed (BitmapHunter hunter) void dispatchNetworkStateChange (NetworkInfo info)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void performSubmit (Action action, boolean dismissFailed) { if (pausedTags.contains(action.getTag())) { pausedActions.put(action.getTarget(), action); return ; } BitmapHunter hunter = hunterMap.get(action.getKey()); if (hunter != null ) { hunter.attach(action); return ; } if (service.isShutdown()) { return ; } 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()); } }
(1) 判断当前tag的请求是已经暂停,如果已经暂停添加到暂停队列中。 (2) BitmapHunter其实实现Runnable,是图片请求任务,判断图片资源的请求是否存在,如果存在将action保存到BitmapHunter中,等图片请求完成后一起处理。 (3)创建BitmapHunter,请求图片,保存到hunterMap中。
1 2 3 4 5 6 7 8 9 10 void performComplete (BitmapHunter hunter) {if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) { cache.set(hunter.getKey(), hunter.getResult()); } hunterMap.remove(hunter.getKey()); batch(hunter); if (hunter.getPicasso().loggingEnabled) { log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion" ); } }
请求完成以后将结果添加到缓存中,hunterMap清除当前hunter。
3.2.6 RequestHandler.java 请求处理。包括网络请求,本地文件请求等。默认有6个实现类: ContactsPhotoRequestHandler(context); MediaStoreRequestHandler(context); ContentStreamRequestHandler(context); AssetRequestHandler(context); FileRequestHandler(context); NetworkRequestHandler(dispatcher.downloader, stats);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static void calculateInSampleSize (int reqWidth, int reqHeight, int width, int height, BitmapFactory.Options options, Request request) {int sampleSize = 1 ;if (height > reqHeight || width > reqWidth) { final int heightRatio; final int widthRatio; if (reqHeight == 0 ) { sampleSize = (int ) Math.floor((float ) width / (float ) reqWidth); } else if (reqWidth == 0 ) { sampleSize = (int ) Math.floor((float ) height / (float ) reqHeight); } else { heightRatio = (int ) Math.floor((float ) height / (float ) reqHeight); widthRatio = (int ) Math.floor((float ) width / (float ) reqWidth); sampleSize = request.centerInside ? Math.max(heightRatio, widthRatio) : Math.min(heightRatio, widthRatio); } } options.inSampleSize = sampleSize; options.inJustDecodeBounds = false ; }
基本的图片压缩算法,算出压缩比,通过BitmapFactory压缩图片,但是这样的压缩不够全面,只有整数倍压缩,所以压缩过后还需要进行二次压缩,后面将出现二次压缩的算法。
1 2 3 4 public abstract Result load (Request request, int networkPolicy) throws IOException ;public abstract boolean canHandleRequest (Request data) ;
3.2.7 RequestCreator.java 1 2 private final Request.Builder data;
图片压缩完成以后,取中间的那一块,如下图。
1 public RequestCreator centerCrop ()
图片会按设置的尺寸,根据宽高比例压缩。与centerCrop不能同时使用
1 public RequestCreator centerInside ()
根据ImageView的大小设置图片压缩的大小
1 public RequestCreator fit ()
直接设置图片压缩的大小,与fit不可同时使用
1 public RequestCreator resize
设置压缩后的图片自定义处理接口
1 public RequestCreator transform (Transformation transformation)
设置请求前的图片
1 public RequestCreator placeholder ()
设置请求错误的图片
1 public RequestCreator error ()
将图片显示在设置的View上面
1 2 3 4 public void into (ImageView target, Callback callback) public void into (Target target)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public void into (ImageView target, Callback callback) {long started = System.nanoTime();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()); } 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 = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade); picasso.enqueueAndSubmit(action); }
(1)deferred表示自动获取ImageView的大小,但是由于ImageView设置wrap_content的时候是获取不到ImageView的大小,所以添加到延迟加载的队列中,当ImageView显示到屏幕以后会开启任务。如果xml中固定了ImageView的大小直接将大小获取,保存起来。 (2)创建Request,根据Request获取缓存数据的key,判断缓存是否存在,存在直接返回。 (3)如果缓存不存在,创建ImageViewAction,提交Action加载图片。
3.2.8 Action.java 1 2 3 4 5 final Picasso picasso;final Request request;final WeakReference<T> target;final String key;final Object tag;
3.2.9 BitmapHunter.java Bitmap请求Runnable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Override public void run () { try { updateThreadName(data); result = hunt(); if (result == null ) { dispatcher.dispatchFailed(this ); } else { dispatcher.dispatchComplete(this ); } } catch (Downloader.ResponseException e) { dispatcher.dispatchFailed(this ); } catch (NetworkRequestHandler.ContentLengthException e) { dispatcher.dispatchRetry(this ); } catch (IOException 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) { dispatcher.dispatchFailed(this ); } finally { } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 Bitmap hunt () throws IOException { Bitmap bitmap = null ; if (shouldReadFromMemoryCache(memoryPolicy)) { bitmap = cache.get(key); if (bitmap != null ) { stats.dispatchCacheHit(); loadedFrom = MEMORY; return bitmap; } } 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 (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; }
这个方法是处理图片最核心的方法,包括请求图片、压缩图片、处理图片。 (1)首先还是会校验内存缓存中是否已经存在。 (2)requestHandler很重要,是请求图片的工具类,看forRequest这个方法,首先获取所有的RequestHandler, 之前有说过默认五种RequestHandler,然后获取可以处理请求的RequestHandler。 (3)生成压缩后的bitmap (整数倍)。 (4)处理图片的转向,小数倍压缩。 (5)自定义图片处理。 (6)分发结果
1 2 3 4 5 6 7 8 static BitmapHunter forRequest () ;static Bitmap decodeStream (InputStream stream, Request request) throws IOException;static Bitmap transformResult (Request data, Bitmap result, int exifRotation)
3.杂谈