作者:谭东
QQ:852041173
Picasso是美国SQUARE移动支付公司开源的图片缓存加载库。可以实现图片下载和缓存功能,效率和性能都很不错。
Square公司官方博客:http://square.github.io/
Square公司Github地址:https://github.com/square
Square公司Picasso的Wiki地址:http://square.github.io/picasso/
Square公司Picasso的Github地址:https://github.com/square/picasso
Picasso源码原理解析:
先看流程图:
Picasso的源码很小,类也很少,相对简单些。
我们主要关注以下标红的几个类。
不是太想写太多的文字,那就看源码分析吧。
先看构造单例模式的Piasso实例:
- /**
- * with方式
- */
- Picasso.with(this).load("http://square.github.io/picasso/static/sample.png").centerCrop().into(imageView);
- /**
- * new with方式
- */
- Picasso picasso = Picasso.with(this);
- /**
- * builder方式
- */
- picasso = new Picasso.Builder(this).executor(threadPoolExecutor).downloader(new OkHttp3Downloader(okHttpClient)).build();//默认3个线程
- Picasso.setSingletonInstance(picasso);
这几种方式通过查看,都是通过一个方式实现的构造,那就是builder方式。
我们按照这句最基本的方式进行源码分析。
- Picasso.with(this).load("http://square.github.io/picasso/static/sample.png").centerCrop().into(imageView);
点击with方法进去。
- public static Picasso with(Context context) {
- if (singleton == null) {
- synchronized (Picasso.class) {
- if (singleton == null) {
- singleton = new Builder(context).build();
- }
- }
- }
- return singleton;
- }
- /** Create the {@link Picasso} instance. */
- public Picasso build() {
- Context context = this.context;
- //是否自定义下载器,无的话使用默认下载器
- if (downloader == null) {
- downloader = Utils.createDefaultDownloader(context);
- }
- //是否自定义缓存方式,无的话使用默认LruCache
- if (cache == null) {
- cache = new LruCache(context);
- }
- //是否自定义线程池,无的话使用默认线程池
- if (service == null) {
- service = new PicassoExecutorService();
- }
- //是否自定义转换器,无的话使用默认转换器
- if (transformer == null) {
- transformer = RequestTransformer.IDENTITY;
- }
- //统计器,统计缓存的命中率
- Stats stats = new Stats(cache);
- //创建Dispather,分发调度器
- Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
- //创建Picasso实例
- return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
- defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
- }
- }
可以看出都是通过builder方式实例化的Picasso类。这里实例化了Dispatcher和Picasso。
接下来点击load方法进去:
- /**
- * Start an image request using the specified path. This is a convenience method for calling
- * {@link #load(Uri)}.
- * <p>
- * This path may be a remote URL, file resource (prefixed with {@code file:}), content resource
- * (prefixed with {@code content:}), or android resource (prefixed with {@code
- * android.resource:}.
- * <p>
- * Passing {@code null} as a {@code path} will not trigger any request but will set a
- * placeholder, if one is specified.
- *
- * @see #load(Uri)
- * @see #load(File)
- * @see #load(int)
- * @throws IllegalArgumentException if {@code path} is empty or blank string.
- */
- 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));
- }
- /**
- * Start an image request using the specified URI.
- * <p>
- * Passing {@code null} as a {@code uri} will not trigger any request but will set a placeholder,
- * if one is specified.
- *
- * @see #load(File)
- * @see #load(String)
- * @see #load(int)
- */
- public RequestCreator load(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);
- }
- /** Fluent API for building an image download request. */
- @SuppressWarnings("UnusedDeclaration") // Public API.
- public class RequestCreator {
- private static final AtomicInteger nextId = new AtomicInteger();
- private final Picasso picasso;
- private final Request.Builder data;
- private boolean noFade;
- private boolean deferred;
- private boolean setPlaceholder = true;
- private int placeholderResId;
- private int errorResId;
- private int memoryPolicy;
- private int networkPolicy;
- private Drawable placeholderDrawable;
- private Drawable errorDrawable;
- private Object tag;
我们接下来点击centerCrop方法进去:
- /**
- * Crops an image inside of the bounds specified by {@link #resize(int, int)} rather than
- * distorting the aspect ratio. This cropping technique scales the image so that it fills the
- * requested bounds and then crops the extra.
- */
- public RequestCreator centerCrop() {
- data.centerCrop();
- return this;
- }
这个配置也是操作我们的RequestCreator。
最后点击into方法。
- /**
- * Asynchronously fulfills the request into the specified {@link ImageView}.
- * <p>
- * <em>Note:</em> This method keeps a weak reference to the {@link ImageView} instance and will
- * automatically support object recycling.
- */
- public void into(ImageView target) {
- into(target, null);
- }
- **
- * Asynchronously fulfills the request into the specified {@link ImageView} and invokes the
- * target {@link Callback} if it's not {@code null}.
- * <p>
- * <em>Note:</em> The {@link Callback} param is a strong reference and will prevent your
- * {@link android.app.Activity} or {@link android.app.Fragment} from being garbage collected. If
- * you use this method, it is <b>strongly</b> recommended you invoke an adjacent
- * {@link Picasso#cancelRequest(android.widget.ImageView)} call to prevent temporary leaking.
- */
- public void into(ImageView target, Callback callback) {
- long started = System.nanoTime();
- checkMain();
- if (target == null) {
- throw new IllegalArgumentException("Target must not be null.");
- }
- 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();
- 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);//时间戳为Key,创建Request
- 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,这里用的ImageViewAction
- Action action =
- new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
- errorDrawable, requestKey, tag, callback, noFade);
- //把请求封装成Action,提交分发
- picasso.enqueueAndSubmit(action);
- }
- 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分发
- dispatcher.dispatchSubmit(action);
- }
- void dispatchSubmit(Action action) {
- handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
- }
- private 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;
- }
- case TAG_PAUSE: {
- Object tag = msg.obj;
- dispatcher.performPauseTag(tag);
- break;
- }
- case TAG_RESUME: {
- Object tag = msg.obj;
- dispatcher.performResumeTag(tag);
- break;
- }
- case HUNTER_COMPLETE: {
- BitmapHunter hunter = (BitmapHunter) msg.obj;
- dispatcher.performComplete(hunter);
- break;
- }
- case HUNTER_RETRY: {
- BitmapHunter hunter = (BitmapHunter) msg.obj;
- dispatcher.performRetry(hunter);
- break;
- }
- case HUNTER_DECODE_FAILED: {
- BitmapHunter hunter = (BitmapHunter) msg.obj;
- dispatcher.performError(hunter, false);
- break;
- }
- case HUNTER_DELAY_NEXT_BATCH: {
- dispatcher.performBatchComplete();
- break;
- }
- case NETWORK_STATE_CHANGE: {
- NetworkInfo info = (NetworkInfo) msg.obj;
- dispatcher.performNetworkStateChange(info);
- break;
- }
- case AIRPLANE_MODE_CHANGE: {
- dispatcher.performAirplaneModeChange(msg.arg1 == AIRPLANE_MODE_ON);
- break;
- }
- default:
- Picasso.HANDLER.post(new Runnable() {
- @Override public void run() {
- throw new AssertionError("Unknown handler message received: " + msg.what);
- }
- });
- }
- }
- }
- void performSubmit(Action action) {
- performSubmit(action, true);
- }
- void performSubmit(Action action, boolean dismissFailed) {
- 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,核心,它是个线程,执行图片下载编解码
- BitmapHunter hunter = hunterMap.get(action.getKey());
- if (hunter != null) {
- hunter.attach(action);
- return;
- }
- if (service.isShutdown()) {
- if (action.getPicasso().loggingEnabled) {
- log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
- }
- return;
- }
- hunter = forRequest(action.getPicasso(), this, cache, stats, action);
- hunter.future = service.submit(hunter);//这个service是ExecutorService线程池,提交执行下载线程
- hunterMap.put(action.getKey(), hunter);
- if (dismissFailed) {
- failedActions.remove(action.getTarget());
- }
- if (action.getPicasso().loggingEnabled) {
- log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
- }
- }
大概看下BitmapHunter源码:
- /*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.squareup.picasso;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Matrix;
- import android.net.NetworkInfo;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.PrintWriter;
- import java.io.StringWriter;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.concurrent.Future;
- import java.util.concurrent.atomic.AtomicInteger;
- import static com.squareup.picasso.MemoryPolicy.shouldReadFromMemoryCache;
- import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;
- import static com.squareup.picasso.Picasso.Priority;
- import static com.squareup.picasso.Picasso.Priority.LOW;
- import static com.squareup.picasso.Utils.OWNER_HUNTER;
- import static com.squareup.picasso.Utils.VERB_DECODED;
- import static com.squareup.picasso.Utils.VERB_EXECUTING;
- import static com.squareup.picasso.Utils.VERB_JOINED;
- import static com.squareup.picasso.Utils.VERB_REMOVED;
- import static com.squareup.picasso.Utils.VERB_TRANSFORMED;
- import static com.squareup.picasso.Utils.getLogIdsForHunter;
- import static com.squareup.picasso.Utils.log;
- class 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();
- private static final ThreadLocal<StringBuilder> NAME_BUILDER = new ThreadLocal<StringBuilder>() {
- @Override protected StringBuilder initialValue() {
- return new StringBuilder(Utils.THREAD_PREFIX);
- }
- };
- private static final AtomicInteger SEQUENCE_GENERATOR = new AtomicInteger();
- private 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);
- }
- };
- final int sequence;
- final Picasso picasso;
- final Dispatcher dispatcher;
- final Cache cache;
- final Stats stats;
- final String key;
- final Request data;
- final int memoryPolicy;
- int networkPolicy;
- final RequestHandler requestHandler;
- Action action;
- List<Action> actions;
- Bitmap result;
- Future<?> future;
- Picasso.LoadedFrom loadedFrom;
- Exception exception;
- int exifRotation; // Determined during decoding of original resource.
- int retryCount;
- Priority priority;
- BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action,
- RequestHandler requestHandler) {
- this.sequence = SEQUENCE_GENERATOR.incrementAndGet();
- this.picasso = picasso;
- this.dispatcher = dispatcher;
- this.cache = cache;
- this.stats = stats;
- this.action = action;
- this.key = action.getKey();
- this.data = action.getRequest();
- this.priority = action.getPriority();
- this.memoryPolicy = action.getMemoryPolicy();
- this.networkPolicy = action.getNetworkPolicy();
- this.requestHandler = requestHandler;
- this.retryCount = requestHandler.getRetryCount();
- }
- @Override public void run() {
- try {
- updateThreadName(data);
- if (picasso.loggingEnabled) {
- log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
- }
- //这个result是Bitmap,调用了hunt方法获取
- result = hunt();
- 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);
- }
- }
- 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.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 {//inputstream解码
- 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;
- }
- /**
- * Decode a byte stream into a Bitmap. This method will take into account additional information
- * about the supplied request in order to do the decoding efficiently (such as through leveraging
- * {@code inSampleSize}).
- */
- static Bitmap decodeStream(InputStream stream, Request request) throws IOException {
- MarkableInputStream markStream = new MarkableInputStream(stream);
- stream = markStream;
- long mark = markStream.savePosition(65536); // TODO fix this crap.
- final BitmapFactory.Options options = RequestHandler.createBitmapOptions(request);
- final boolean calculateSize = RequestHandler.requiresInSampleSize(options);
- boolean isWebPFile = Utils.isWebPFile(stream);
- markStream.reset(mark);
- // When decode WebP network stream, BitmapFactory throw JNI Exception and make app crash.
- // Decode byte array instead
- if (isWebPFile) {
- byte[] bytes = Utils.toByteArray(stream);
- if (calculateSize) {
- BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
- RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
- request);
- }
- return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
- } else {
- if (calculateSize) {
- BitmapFactory.decodeStream(stream, null, options);
- RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
- request);
- markStream.reset(mark);
- }
- Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
- if (bitmap == null) {
- // Treat null as an IO exception, we will eventually retry.
- throw new IOException("Failed to decode stream.");
- }
- return bitmap;
- }
- }
接下来,看dispatcher派发加载成功消息的逻辑:
- void dispatchComplete(BitmapHunter hunter) {
- handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
- }
- case HUNTER_COMPLETE: {//加载完成
- BitmapHunter hunter = (BitmapHunter) msg.obj;
- dispatcher.performComplete(hunter);
- break;
- }
- 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");
- }
- }
- 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);
- }
- }
- case HUNTER_DELAY_NEXT_BATCH: {//继续派发next_batch消息
- dispatcher.performBatchComplete();
- break;
- }
- void performBatchComplete() {
- List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
- batch.clear();
- mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));//注意这里的mainThreadHandler是Picasso的Handler
- logBatch(copy);
- }
注意这里的mainThreadHander是Picasso里的Handler,我们可以看下Dispatcher的实例化。
- Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
- Downloader downloader, Cache cache, Stats stats) {
- this.dispatcherThread = new DispatcherThread();
- this.dispatcherThread.start();
- Utils.flushStackLocalLeaks(dispatcherThread.getLooper());
- this.context = context;
- this.service = service;
- this.hunterMap = new LinkedHashMap<String, BitmapHunter>();
- this.failedActions = new WeakHashMap<Object, Action>();
- this.pausedActions = new WeakHashMap<Object, Action>();
- this.pausedTags = new HashSet<Object>();
- this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
- this.downloader = downloader;
- this.mainThreadHandler = mainThreadHandler;//这个就是Picasso传进来的mainThreadHandler
- this.cache = cache;
- this.stats = stats;
- this.batch = new ArrayList<BitmapHunter>(4);
- this.airplaneMode = Utils.isAirplaneModeOn(this.context);
- this.scansNetworkChanges = hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE);
- this.receiver = new NetworkBroadcastReceiver(this);
- receiver.register();
- }
那我们看下Picasso里的HUNTER_BATCH_COMPLETE消息处理:
- static final String TAG = "Picasso";
- static 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);//回调了complete方法
- }
- break;
- }
- case REQUEST_GCED: {
- Action action = (Action) msg.obj;
- if (action.getPicasso().loggingEnabled) {
- log(OWNER_MAIN, VERB_CANCELED, action.request.logId(), "target got garbage collected");
- }
- action.picasso.cancelExistingRequest(action.getTarget());
- break;
- }
- case REQUEST_BATCH_RESUME:
- @SuppressWarnings("unchecked") List<Action> batch = (List<Action>) msg.obj;
- //noinspection ForLoopReplaceableByForEach
- for (int i = 0, n = batch.size(); i < n; i++) {
- Action action = batch.get(i);
- action.picasso.resumeAction(action);
- }
- break;
- default:
- throw new AssertionError("Unknown handler message received: " + msg.what);
- }
- }
- };
我们看下Picasso的complete方法:
- void complete(BitmapHunter hunter) {
- Action single = hunter.getAction();//拿到了派发过来的Action
- 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();//拿到了Bitmap
- LoadedFrom from = hunter.getLoadedFrom();
- if (single != null) {
- deliverAction(result, from, single);//执行deliverAction方法
- }
- if (hasMultiple) {
- //noinspection ForLoopReplaceableByForEach
- for (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.");
- }
- action.complete(result, from);//回调了action的complete方法
- 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());
- }
- }
- }
- /*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.squareup.picasso;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.drawable.Drawable;
- import android.widget.ImageView;
- class ImageViewAction extends Action<ImageView> {
- Callback callback;
- ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
- int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
- Callback callback, boolean noFade) {
- super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,
- tag, noFade);
- this.callback = callback;
- }
- //图片加载完成回调
- @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);//设置bitmap给PicassoDrawable
- if (callback != null) {
- callback.onSuccess();
- }
- }
- @Override public void error() {
- ImageView target = this.target.get();
- if (target == null) {
- return;
- }
- if (errorResId != 0) {
- target.setImageResource(errorResId);
- } else if (errorDrawable != null) {
- target.setImageDrawable(errorDrawable);
- }
- if (callback != null) {
- callback.onError();
- }
- }
- @Override void cancel() {
- super.cancel();
- if (callback != null) {
- callback = null;
- }
- }
- }
- /*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.squareup.picasso;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.ColorFilter;
- import android.graphics.Paint;
- import android.graphics.Path;
- import android.graphics.Point;
- import android.graphics.Rect;
- import android.graphics.drawable.AnimationDrawable;
- import android.graphics.drawable.BitmapDrawable;
- import android.graphics.drawable.Drawable;
- import android.os.Build;
- import android.os.SystemClock;
- import android.widget.ImageView;
- import static android.graphics.Color.WHITE;
- import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;
- final class PicassoDrawable extends BitmapDrawable {//继承自BitmapDrawable
- // Only accessed from main thread.
- private static final Paint DEBUG_PAINT = new Paint();
- private static final float FADE_DURATION = 200f; //ms
- /**
- * Create or update the drawable on the target {@link ImageView} to display the supplied bitmap
- * image.
- */
- static void setBitmap(ImageView target, Context context, Bitmap bitmap,
- Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
- Drawable placeholder = target.getDrawable();
- if (placeholder instanceof AnimationDrawable) {
- ((AnimationDrawable) placeholder).stop();
- }
- PicassoDrawable drawable =
- new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
- target.setImageDrawable(drawable);//调用ImageView的setImageDrawable方法将转换后的drawable加载进去显示
- }
- /**
- * Create or update the drawable on the target {@link ImageView} to display the supplied
- * placeholder image.
- */
- static void setPlaceholder(ImageView target, Drawable placeholderDrawable) {
- target.setImageDrawable(placeholderDrawable);
- if (target.getDrawable() instanceof AnimationDrawable) {
- ((AnimationDrawable) target.getDrawable()).start();
- }
- }
- private final boolean debugging;
- private final float density;
- private final Picasso.LoadedFrom loadedFrom;
- Drawable placeholder;
- long startTimeMillis;
- boolean animating;
- int alpha = 0xFF;
- PicassoDrawable(Context context, Bitmap bitmap, Drawable placeholder,
- Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
- super(context.getResources(), bitmap);
- this.debugging = debugging;
- this.density = context.getResources().getDisplayMetrics().density;
- this.loadedFrom = loadedFrom;
- boolean fade = loadedFrom != MEMORY && !noFade;
- if (fade) {
- this.placeholder = placeholder;
- animating = true;
- startTimeMillis = SystemClock.uptimeMillis();
- }
- }
- @Override public void draw(Canvas canvas) {
- if (!animating) {
- super.draw(canvas);
- } else {
- float normalized = (SystemClock.uptimeMillis() - startTimeMillis) / FADE_DURATION;
- if (normalized >= 1f) {
- animating = false;
- placeholder = null;
- super.draw(canvas);
- } else {
- if (placeholder != null) {
- placeholder.draw(canvas);
- }
- int partialAlpha = (int) (alpha * normalized);
- super.setAlpha(partialAlpha);
- super.draw(canvas);
- super.setAlpha(alpha);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
- invalidateSelf();
- }
- }
- }
- if (debugging) {
- drawDebugIndicator(canvas);
- }
- }
- @Override public void setAlpha(int alpha) {
- this.alpha = alpha;
- if (placeholder != null) {
- placeholder.setAlpha(alpha);
- }
- super.setAlpha(alpha);
- }
- @Override public void setColorFilter(ColorFilter cf) {
- if (placeholder != null) {
- placeholder.setColorFilter(cf);
- }
- super.setColorFilter(cf);
- }
- @Override protected void onBoundsChange(Rect bounds) {
- if (placeholder != null) {
- placeholder.setBounds(bounds);
- }
- super.onBoundsChange(bounds);
- }
- private void drawDebugIndicator(Canvas canvas) {
- DEBUG_PAINT.setColor(WHITE);
- Path path = getTrianglePath(new Point(0, 0), (int) (16 * density));
- canvas.drawPath(path, DEBUG_PAINT);
- DEBUG_PAINT.setColor(loadedFrom.debugColor);
- path = getTrianglePath(new Point(0, 0), (int) (15 * density));
- canvas.drawPath(path, DEBUG_PAINT);
- }
- private static Path getTrianglePath(Point p1, int width) {
- Point p2 = new Point(p1.x + width, p1.y);
- Point p3 = new Point(p1.x, p1.y + width);
- Path path = new Path();
- path.moveTo(p1.x, p1.y);
- path.lineTo(p2.x, p2.y);
- path.lineTo(p3.x, p3.y);
- return path;
- }
- }
Picasso基本用法:
http://blog.csdn.net/jay100500/article/details/70767007