网站建设内容保障工作个人总结怎样做企业手机网站首页
本文采用研究生论文格式编写,方便大家阅读。作者:谭东
 
 
摘要
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
 
1 绪论
Picasso的用法是链式调用,和Glide用法很像。当然也支持个性化设置,是目前比较流行的图片缓存库之一。
 
1.1 Picasso特点
Picasso是全尺寸下载图片,也就是把要加载的图片原图下载缓存下来。Picasso默认的缓存格式为ARGB_888,相对于RGB_565内存占用高了一半左右,当然图片质量显示要高一些。下面看下Picasso的主要特点:
①链式调用,使用简单;
②具有一般图片框架的基础功能;
③方便的图片转换;
④加载过程监听和错误处理;
⑤自动添加磁盘和内存二级缓存;
⑥支持多种数据源加载。
Picasso默认不支持Gif图片加载。Picasso库很小,类也很少,库仅118KB大小。最新版目前是2.5.2。
 
2 Picasso基本用法
 
2.1 引入Picasso
Jar包下载:https://search.maven.org/remote_content?g=com.squareup.picasso&a=picasso&v=LATEST
Gradle引用:
- compile 'com.squareup.picasso:picasso:2.5.2'
 
- <dependency>
 - <groupId>com.squareup.picasso</groupId>
 - <artifactId>picasso</artifactId>
 - <version>2.5.2</version>
 - </dependency>
 
 
2.2 基本加载图片
图2.1 Picasso加载图片
 
链式调用,逻辑上比较清晰。
- Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
 
改变图片尺寸和填充方式:
- Picasso.with(context)
 - .load(url)
 - .resize(50, 50)
 - .centerCrop()
 - .into(imageView)
 
- public class CropSquareTransformation implements Transformation {
 - @Override public Bitmap transform(Bitmap source) {
 - int size = Math.min(source.getWidth(), source.getHeight());
 - int x = (source.getWidth() - size) / 2;
 - int y = (source.getHeight() - size) / 2;
 - Bitmap result = Bitmap.createBitmap(source, x, y, size, size);
 - if (result != source) {
 - source.recycle();
 - }
 - return result;
 - }
 - @Override public String key() { return "square()"; }
 - }
 
- Picasso.with(context)
 - .load(url)
 - .placeholder(R.drawable.user_placeholder)
 - .error(R.drawable.user_placeholder_error)
 - .into(imageView);
 
- Picasso.with(context).load(R.drawable.landing_screen).into(imageView1);
 - Picasso.with(context).load("file:///android_asset/DvpvklR.png").into(imageView2);
 - Picasso.with(context).load(new File(...)).into(imageView3);
 
- Picasso picasso = Picasso.with(this);
 - picasso.setIndicatorsEnabled(true);//蓝色:从本地缓存读取的图片,红色:从网络加载的图片,绿色:从内存缓存加载的图片
 - picasso.setLoggingEnabled(true);//日志调试模式
 
图2.2 Picasso加载图片来源标识
 
图片缓存颜色格式配置:
- Picasso.with(context).load("http://square.github.io/picasso/static/sample.png").config(Bitmap.Config.RGB_565)
 - .into(imageView);
 
 
3 Picasso高级用法
下面的代码是实现:
1.自定义Picasso全局线程池
  2.自定义Picasso默认缓存目录
  3.自定义监听Picasso加载图片进度,结合OKHTTP
 
- package com.tandong.picassodemo;
 - import android.app.Application;
 - import com.tandong.picassodemo.utils.OkHttp3Downloader;
 - import com.tandong.picassodemo.utils.ProgressListener;
 - import com.squareup.picasso.Picasso;
 - import java.io.IOException;
 - import java.util.concurrent.PriorityBlockingQueue;
 - import java.util.concurrent.ThreadPoolExecutor;
 - import java.util.concurrent.TimeUnit;
 - import okhttp3.Cache;
 - import okhttp3.Interceptor;
 - import okhttp3.OkHttpClient;
 - import okhttp3.Response;
 - /**
 - * Created by Administrator on 2017/4/12.
 - */
 - /**
 - * 1.自定义Picasso全局线程池
 - * 2.自定义Picasso默认缓存目录
 - * 3.自定义监听Picasso加载图片进度,结合OKHTTP
 - */
 - public class BaseApplication extends Application {
 - private ThreadPoolExecutor threadPoolExecutor;
 - private int cpu = 0;
 - private Picasso picasso;
 - private OkHttpClient okHttpClient;
 - public static ProgressListener progress;
 - @Override
 - public void onCreate() {
 - super.onCreate();
 - initPicasso();
 - }
 - private void initPicasso() {
 - okHttpClient = new OkHttpClient.Builder().addNetworkInterceptor(new Interceptor() {
 - @Override
 - public Response intercept(Chain chain) throws IOException {
 - Response response = chain.proceed(chain.request());
 - return response.newBuilder().body(new ProgressResponseBody(progress, response.body())).build();
 - }
 - }).cache(new Cache(getExternalCacheDir(), 10 * 1024 * 1024)).build();
 - cpu = Runtime.getRuntime().availableProcessors();
 - threadPoolExecutor = new ThreadPoolExecutor(cpu + 1, cpu * 2 + 1, 1, TimeUnit.MINUTES, new PriorityBlockingQueue<Runnable>());
 - picasso = new Picasso.Builder(this).executor(threadPoolExecutor).downloader(new OkHttp3Downloader(okHttpClient)).build();//默认3个线程
 - Picasso.setSingletonInstance(picasso);
 - }
 - public static void setProgressListener(ProgressListener progressListener) {
 - progress = progressListener;
 - }
 - }
 
build.gradle引用:
- compile 'com.squareup.picasso:picasso:2.5.2'
 - compile 'com.squareup.okhttp3:okhttp:3.6.0'
 
 
- package com.tandong.picassodemo.utils;
 - import android.content.Context;
 - import android.net.Uri;
 - import android.os.StatFs;
 - import com.squareup.picasso.Downloader;
 - import com.squareup.picasso.NetworkPolicy;
 - import java.io.File;
 - import java.io.IOException;
 - import okhttp3.Cache;
 - import okhttp3.CacheControl;
 - import okhttp3.Call;
 - import okhttp3.OkHttpClient;
 - import okhttp3.Request;
 - import okhttp3.ResponseBody;
 - /**
 - * Created by office on 2017/4/10.
 - */
 - public final class OkHttp3Downloader implements Downloader {
 - private static final String PICASSO_CACHE = "picasso-cache";
 - private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB
 - private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
 - private static File defaultCacheDir(Context context) {
 - File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);
 - if (!cache.exists()) {
 - //noinspection ResultOfMethodCallIgnored
 - cache.mkdirs();
 - }
 - return cache;
 - }
 - private static long calculateDiskCacheSize(File dir) {
 - long size = MIN_DISK_CACHE_SIZE;
 - try {
 - StatFs statFs = new StatFs(dir.getAbsolutePath());
 - long available = ((long) statFs.getBlockCount()) * statFs.getBlockSize();
 - // Target 2% of the total space.
 - size = available / 50;
 - } catch (IllegalArgumentException ignored) {
 - }
 - // Bound inside min/max size for disk cache.
 - return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);
 - }
 - /**
 - * Creates a {@link Cache} that would have otherwise been created by calling
 - * {@link #OkHttp3Downloader(Context)}. This allows you to build your own {@link OkHttpClient}
 - * while still getting the default disk cache.
 - */
 - public static Cache createDefaultCache(Context context) {
 - File dir = defaultCacheDir(context);
 - return new Cache(dir, calculateDiskCacheSize(dir));
 - }
 - private static OkHttpClient createOkHttpClient(File cacheDir, long maxSize) {
 - return new OkHttpClient.Builder()
 - .cache(new Cache(cacheDir, maxSize))
 - .build();
 - }
 - private final Call.Factory client;
 - private final Cache cache;
 - /**
 - * Create new downloader that uses OkHttp. This will install an image cache into your application
 - * cache directory.
 - */
 - public OkHttp3Downloader(Context context) {
 - this(defaultCacheDir(context));
 - }
 - /**
 - * Create new downloader that uses OkHttp. This will install an image cache into the specified
 - * directory.
 - *
 - * @param cacheDir The directory in which the cache should be stored
 - */
 - public OkHttp3Downloader(File cacheDir) {
 - this(cacheDir, calculateDiskCacheSize(cacheDir));
 - }
 - /**
 - * Create new downloader that uses OkHttp. This will install an image cache into your application
 - * cache directory.
 - *
 - * @param maxSize The size limit for the cache.
 - */
 - public OkHttp3Downloader(final Context context, final long maxSize) {
 - this(defaultCacheDir(context), maxSize);
 - }
 - /**
 - * Create new downloader that uses OkHttp. This will install an image cache into the specified
 - * directory.
 - *
 - * @param cacheDir The directory in which the cache should be stored
 - * @param maxSize The size limit for the cache.
 - */
 - public OkHttp3Downloader(File cacheDir, long maxSize) {
 - this(createOkHttpClient(cacheDir, maxSize));
 - }
 - public OkHttp3Downloader(OkHttpClient client) {
 - this.client = client;
 - this.cache = client.cache();
 - }
 - public OkHttp3Downloader(Call.Factory client) {
 - this.client = client;
 - this.cache = null;
 - }
 - @Override
 - public Response load(Uri uri, int networkPolicy) throws IOException {
 - CacheControl cacheControl = null;
 - if (networkPolicy != 0) {
 - if (NetworkPolicy.isOfflineOnly(networkPolicy)) {
 - cacheControl = CacheControl.FORCE_CACHE;
 - } else {
 - CacheControl.Builder builder = new CacheControl.Builder();
 - if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {
 - builder.noCache();
 - }
 - if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {
 - builder.noStore();
 - }
 - cacheControl = builder.build();
 - }
 - }
 - Request.Builder builder = new Request.Builder().url(uri.toString());
 - if (cacheControl != null) {
 - builder.cacheControl(cacheControl);
 - }
 - okhttp3.Response response = client.newCall(builder.build()).execute();
 - int responseCode = response.code();
 - if (responseCode >= 300) {
 - response.body().close();
 - throw new ResponseException(responseCode + " " + response.message(), networkPolicy,
 - responseCode);
 - }
 - boolean fromCache = response.cacheResponse() != null;
 - ResponseBody responseBody = response.body();
 - return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength());
 - }
 - @Override
 - public void shutdown() {
 - if (cache != null) {
 - try {
 - cache.close();
 - } catch (IOException ignored) {
 - }
 - }
 - }
 - }
 
自定义ResponseBody,实现对数据下载进度的监听处理:
- package com.tandong.picassodemo;
 - import android.util.Log;
 - import com.tandong.picassodemo.utils.ProgressListener;
 - import java.io.IOException;
 - import okhttp3.MediaType;
 - import okhttp3.ResponseBody;
 - import okio.Buffer;
 - import okio.BufferedSource;
 - import okio.ForwardingSource;
 - import okio.Okio;
 - import okio.Source;
 - /**
 - * Created by Administrator on 2017/4/14.
 - */
 - public class ProgressResponseBody extends ResponseBody {
 - private ResponseBody responseBody;
 - private BufferedSource bufferedSource;
 - private ProgressListener progressListener;
 - public ProgressResponseBody(ProgressListener progressListener,ResponseBody responseBody) {
 - this.responseBody = responseBody;
 - this.progressListener=progressListener;
 - }
 - @Override
 - public MediaType contentType() {
 - return responseBody.contentType();
 - }
 - @Override
 - public long contentLength() {
 - return responseBody.contentLength();
 - }
 - @Override
 - public BufferedSource source() {
 - if (bufferedSource == null) {
 - bufferedSource = Okio.buffer(source(responseBody.source()));
 - }
 - return bufferedSource;
 - }
 - private Source source(Source source) {
 - ForwardingSource forwardingSource = new ForwardingSource(source) {
 - long totalBytesRead = 0L;
 - @Override
 - public long read(Buffer sink, long byteCount) throws IOException {
 - long bytesRead = super.read(sink, byteCount);
 - totalBytesRead += bytesRead != -1 ? bytesRead : 0;
 - progressListener.update((int) ((100 * totalBytesRead) / responseBody.contentLength()));
 - Log.i("info", "进度:" + (int) ((100 * totalBytesRead) / responseBody.contentLength()));
 - return bytesRead;
 - }
 - @Override
 - public void close() throws IOException {
 - super.close();
 - }
 - };
 - return forwardingSource;
 - }
 - }
 
- package com.tandong.picassodemo.utils;
 - import android.support.annotation.IntRange;
 - /**
 - * Created by Administrator on 2017/4/14.
 - */
 - public interface ProgressListener {
 - public void update(@IntRange(from = 0, to = 100) int progress);
 - }
 
- package com.tandong.picassodemo.view;
 - import android.content.Context;
 - import android.graphics.Canvas;
 - import android.graphics.Color;
 - import android.graphics.Paint;
 - import android.graphics.RectF;
 - import android.support.annotation.IntRange;
 - import android.support.v4.view.ViewCompat;
 - import android.util.AttributeSet;
 - import android.widget.ImageView;
 - /**
 - * Created by office on 2017/4/10.
 - */
 - public class PicassoImageView extends ImageView {
 - private final int MAX_PROGRESS = 100;
 - private Paint mArcPaint;
 - private RectF mBound;
 - private Paint mCirclePaint;
 - private int mProgress = 0;
 - public PicassoImageView(Context context) {
 - this(context, null, 0);
 - }
 - public PicassoImageView(Context context, AttributeSet attrs) {
 - this(context, attrs, 0);
 - }
 - public PicassoImageView(Context context, AttributeSet attrs, int defStyleAttr) {
 - super(context, attrs, defStyleAttr);
 - init();
 - }
 - private void init() {
 - mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 - mArcPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 - mArcPaint.setStrokeWidth(dpToPixel(0.1f, getContext()));
 - mArcPaint.setColor(Color.argb(120, 0xff, 0xff, 0xff));
 - mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 - mCirclePaint.setStyle(Paint.Style.STROKE);
 - mCirclePaint.setStrokeWidth(dpToPixel(2, getContext()));
 - mCirclePaint.setColor(Color.argb(120, 0xff, 0xff, 0xff));
 - mBound = new RectF();
 - }
 - public void setProgress(@IntRange(from = 0, to = MAX_PROGRESS) int mProgress) {
 - this.mProgress = mProgress;
 - ViewCompat.postInvalidateOnAnimation(this);
 - }
 - @Override
 - protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 - super.onSizeChanged(w, h, oldw, oldh);
 - int min = Math.min(w, h);
 - int max = w + h - min;
 - int r = min / 5;
 - //set up a square in the imageView
 - //设置了在图片中间的一个小圆
 - mBound.set(max / 2 - r, min / 2 - r, max / 2 + r, min / 2 + r);
 - }
 - @Override
 - protected void onDraw(Canvas canvas) {
 - super.onDraw(canvas);
 - //画进度条, Paint是画笔,详见源码
 - if (mProgress != MAX_PROGRESS && mProgress != 0) {
 - float mAngle = mProgress * 360f / MAX_PROGRESS;
 - //画扇型
 - canvas.drawArc(mBound, 270, mAngle, true, mArcPaint);
 - //画圆
 - canvas.drawCircle(mBound.centerX(), mBound.centerY(), mBound.height() / 2, mCirclePaint);
 - }
 - }
 - private static float scale;
 - public static int dpToPixel(float dp, Context context) {
 - if (scale == 0) {
 - scale = context.getResources().getDisplayMetrics().density;
 - }
 - return (int) (dp * scale);
 - }
 - }
 
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/71055229
 
 
 
 
遵循:BY-SA
署名-相同方式共享 4.0协议
