这次要分析的源码是 Picasso 2.5.2 ,四年前的版本,用eclipse写的,但不影响这次我们对其源码的分析
地址:https://github.com/square/picasso/tree/picasso-parent-2.5.2
Picasso的简单使用
Picasso.with(this)
.load("http://ww3.sinaimg.cn/large/610dc034jw1fasakfvqe1j20u00mhgn2.jpg")
.into(mImageView);
清晰简洁
Picasso的源码分析
Picasso的总体流程:
1)将请求封装为Request对象,然后将Request对象进一步封装为Action(ImageAction)对象。
2)将Action(ImageAction)对象交给Dispather进行分发
3)最终将action交给BitmapHunter这个Runnable作为在线程池中线程的工作的单元(具体的是讲action持有的当前Reqeuest对象)
4)由RequestHandler来处理当前request,调用其load方法将加载完成后的图片交给PicassoDrawable显示图片。
代码流程如下:Picasso->load->创建request->创建action->Dispatcher分发action->RequestHandler的load方法处理具体的请求->PicassoDrawable显示图片。
下面来看细节:
1、Picasso.with()
首先看一下Picasso的构造方法
Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
RequestTransformer requestTransformer, List extraRequestHandlers, Stats stats,
Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
this.context = context;
this.dispatcher = dispatcher;
//配置缓存策略
this.cache = cache;
//配置图片下载监听
this.listener = listener;
//配置自定义的请求转换器
this.requestTransformer = requestTransformer;
//配置自定义的图片设置
this.defaultBitmapConfig = defaultBitmapConfig;
int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
//用户自定义RequestHandler的数量
int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
List allRequestHandlers =
new ArrayList(builtInHandlers + extraCount);
//添加各种RequestHandler
//先添加ResourceRequestHandler
// 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);
this.stats = stats;
this.targetToAction = new WeakHashMap
with()函数通过双重检查锁模式返回默认Picasso默认实例
//通过双重检查锁模式 返回Picasso单例对象
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
//通过建造者模式,对Picasso进行系统默认设置
singleton = new Builder(context).build();
}
}
}
return singleton;
}
通过建造者模式,对Picasso进行系统默认设置
/** Fluent API for creating {@link Picasso} instances. */
@SuppressWarnings("UnusedDeclaration") // Public API.
public static class Builder {
private final Context context;
private Downloader downloader;//配置自定义的义图片下载类
private ExecutorService service;//配置自定义的线程池
private Cache cache;//配置缓存策略
private Listener listener;//配置图片下载监听
private RequestTransformer transformer;//配置图片自定义转换器
private List requestHandlers;//配置RequestHandler 集合
private Bitmap.Config defaultBitmapConfig;//配置默认图片设置
private boolean indicatorsEnabled;
private boolean loggingEnabled;
/** Start building a new {@link Picasso} instance. */
public Builder(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.context = context.getApplicationContext();
}
/**
* Specify the default {@link Bitmap.Config} used when decoding images. This can be overridden
* on a per-request basis using {@link RequestCreator#config(Bitmap.Config) config(..)}.
*/
public Builder defaultBitmapConfig(Bitmap.Config bitmapConfig) {
if (bitmapConfig == null) {
throw new IllegalArgumentException("Bitmap config must not be null.");
}
this.defaultBitmapConfig = bitmapConfig;
return this;
}
/** Specify the {@link Downloader} that will be used for downloading images. */
public Builder downloader(Downloader downloader) {
if (downloader == null) {
throw new IllegalArgumentException("Downloader must not be null.");
}
if (this.downloader != null) {
throw new IllegalStateException("Downloader already set.");
}
this.downloader = downloader;
return this;
}
/**
* Specify the executor service for loading images in the background.
*
* Note: Calling {@link Picasso#shutdown() shutdown()} will not shutdown supplied executors.
*/
public Builder executor(ExecutorService executorService) {
if (executorService == null) {
throw new IllegalArgumentException("Executor service must not be null.");
}
if (this.service != null) {
throw new IllegalStateException("Executor service already set.");
}
this.service = executorService;
return this;
}
/** Specify the memory cache used for the most recent images. */
public Builder memoryCache(Cache memoryCache) {
if (memoryCache == null) {
throw new IllegalArgumentException("Memory cache must not be null.");
}
if (this.cache != null) {
throw new IllegalStateException("Memory cache already set.");
}
this.cache = memoryCache;
return this;
}
/** Specify a listener for interesting events. */
public Builder listener(Listener listener) {
if (listener == null) {
throw new IllegalArgumentException("Listener must not be null.");
}
if (this.listener != null) {
throw new IllegalStateException("Listener already set.");
}
this.listener = listener;
return this;
}
/**
* Specify a transformer for all incoming requests.
*
* NOTE: This is a beta feature. The API is subject to change in a backwards incompatible
* way at any time.
*/
public Builder requestTransformer(RequestTransformer transformer) {
if (transformer == null) {
throw new IllegalArgumentException("Transformer must not be null.");
}
if (this.transformer != null) {
throw new IllegalStateException("Transformer already set.");
}
this.transformer = transformer;
return this;
}
/** Register a {@link RequestHandler}. */
public Builder addRequestHandler(RequestHandler requestHandler) {
if (requestHandler == null) {
throw new IllegalArgumentException("RequestHandler must not be null.");
}
if (requestHandlers == null) {
requestHandlers = new ArrayList();
}
if (requestHandlers.contains(requestHandler)) {
throw new IllegalStateException("RequestHandler already registered.");
}
requestHandlers.add(requestHandler);
return this;
}
/**
* @deprecated Use {@link #indicatorsEnabled(boolean)} instead.
* Whether debugging is enabled or not.
*/
@Deprecated public Builder debugging(boolean debugging) {
return indicatorsEnabled(debugging);
}
/** Toggle whether to display debug indicators on images. */
public Builder indicatorsEnabled(boolean enabled) {
this.indicatorsEnabled = enabled;
return this;
}
/**
* Toggle whether debug logging is enabled.
*
* WARNING: Enabling this will result in excessive object allocation. This should be only
* be used for debugging purposes. Do NOT pass {@code BuildConfig.DEBUG}.
*/
public Builder loggingEnabled(boolean enabled) {
this.loggingEnabled = enabled;
return this;
}
/** Create the {@link Picasso} instance. */
//所有属性设置成默认
public Picasso build() {
Context context = this.context;
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
if (cache == null) {
cache = new LruCache(context);
}
if (service == null) {
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);
}
}
可以看出with()函数主要 使用单例模式(双重检查锁)创建Picasso实例,并使用建造者模式 对Picasso进行初始化配置。
2、Picasso.load()
//创建RequestCreator
public RequestCreator load(Uri uri) {
return new RequestCreator(this, uri, 0);
}
/**
* Start an image request using the specified path. This is a convenience method for calling
* {@link #load(Uri)}.
*
* 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:}.
*
* 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 image file. This is a convenience method for
* calling {@link #load(Uri)}.
*
* Passing {@code null} as a {@code file} will not trigger any request but will set a
* placeholder, if one is specified.
*
* Equivalent to calling {@link #load(Uri) load(Uri.fromFile(file))}.
*
* @see #load(Uri)
* @see #load(String)
* @see #load(int)
*/
public RequestCreator load(File file) {
if (file == null) {
return new RequestCreator(this, null, 0);
}
return load(Uri.fromFile(file));
}
/**
* Start an image request using the specified drawable resource ID.
*
* @see #load(Uri)
* @see #load(String)
* @see #load(File)
*/
public RequestCreator load(int resourceId) {
if (resourceId == 0) {
throw new IllegalArgumentException("Resource ID must not be zero.");
}
return new RequestCreator(this, null, resourceId);
}
可以看出这几种重载方法 目的都是为了创建RequestCreator 对象
//让我们来看看RequestCreator的构造方法
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;
//接着创建了Request对象
this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}
调用load方法的时候实际上并没有对图片资源进行加载,只是简单返回了一个RequestCreator对象,该对象在初始化的时候初始化了Request的Builder对象
3、RequestCreator.into()
使用过Picasso的都知道,我们调用RequestCreator的into方法来完成工作的,那么就先简单的分析RequestCreator的into系列重载方法之一进行说明
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 request = createRequest(started);
//创建请求的key
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);
}
继续查看 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.dispatchSubmit(action);
}
继续跟进 dispatcher.dispatchSubmit(action);
void dispatchSubmit(Action action) {
//这里调用了handler发送消息
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case REQUEST_SUBMIT: {
Action action = (Action) msg.obj;
//重点在这
dispatcher.performSubmit(action);
break;
}
继续跟进dispatcher.performSubmit(action);
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获取了请求的key,Hunter是一个runnable
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;
}
//重点在这 forRequest();
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());
}
}
继续跟进forRequest();
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
Action action) {
//获取当前请求对象
Request request = action.getRequest();
//获取picasso内置的RequestHandler对象和自定义的RequestHandler对象
List requestHandlers = picasso.getRequestHandlers();
//判断内置对象或者自定义的对象哪一个对象能处理当前请求
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
if (requestHandler.canHandleRequest(request)) {//如果能处理当前请求
//返回一个bitMapHandler
//把当前ReqeuestHandler对象交给BitmapHunter
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}
//说明当前请求无效
return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
}
上面forRequest方法也很简单:主要执行了一下逻辑
1、获取当前请求Reqeuest对象
2、获取Picasso内置的RequestHandler以及自定义的RequestHandler集合。
3、判断集合中哪一个RequestHandler对象可以对当前Reqeuest进行处理(canHanlderReqest返回true)
4、把集合中能处理当前Request的RequestHandler对象交给BitMapHunter。
Action的简单说明:
Action所有的属性如下:
final Picasso picasso;//持有上文单利引用
final Request request;//上文提到的request
final WeakReference target;//target可能是ImageView,或者优先理解为ImageView,为弱引用,确保target被回收的时候不受影响
final boolean noFade;
final int memoryPolicy;//缓存策略
final int networkPolicy;//
final int errorResId;
final Drawable errorDrawable;
final String key;
final Object tag;
boolean willReplay;
boolean cancelled;
Action的主要职责就是:对图片进行加载,配置图片的文件缓存和内存缓存策略以及是否重新加载等逻辑。使得责任分明,调理清晰。
Action是一个抽象的泛型类,提供了complete和error两个抽象方法
abstract void complete(Bitmap result, Picasso.LoadedFrom from);
abstract void error();
它的子类又如下几个:
GetAction、FetchAction、ImageViewAction、TargetAction在此处我们提交的是ImageViewAction.
让我们简单的看一下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.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
//设置callback
if (callback != null) {
callback.onSuccess();
}
}
可以发现最终是由PicassoDrawable来完成图片的显示,所以继续跟进:
PicasDrawable是BitmapDrawable的子类:
final class PicassoDrawable extends BitmapDrawable {
static void setBitmap(ImageView target, Context context, Bitmap bitmap,
Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
//获取drawable
Drawable placeholder = target.getDrawable();
if (placeholder instanceof AnimationDrawable) {
((AnimationDrawable) placeholder).stop();
}
//创建
PicassoDrawable drawable =
new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
//显示
target.setImageDrawable(drawable);
}
//最终将图片显示出来
}
RequestHandler
该类用来处理不同来源的图片,是个抽象类。
在picasso只是用面向对象的方式处理来自网络(NetworkRequestHandler),Resoure资源图片(ResourceRequestHandler),asset文件中的图片(AssetRequestHandler)等等若干个Handler。在初始化Picasso的时候,Picasso默认实现的RequestHandler的上述子类是预加载到一个集合中去的!
Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
RequestTransformer requestTransformer, List extraRequestHandlers, Stats stats,
Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
...........................
/用户自定义RequestHandler的数量
int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
List allRequestHandlers =
new ArrayList(builtInHandlers + extraCount);
//添加各种RequestHandler
//先添加ResourceRequestHandler
// 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自己实现的handler在构造器初始化的时候就把自己实现的RequestHander的子类预先初始化好(姑且称之为Picasso内置的ReqeustHandler对象),当然用户也可以实现自己的RequestHandler通过Picasso.Builder.addRequestHandler来添加进去,当然大多数情况不需要自己提供ReqeuestHandler的实现。
那Picasso怎么知道是来自于哪个种类的图片呢?这就是ReqeuestHandler的功能了!RequestHandler时候一个抽象类,从名字上就可以知道它是处理具体请求的,它提供了两个重要的抽象方法来完成对不同种类图片的处理工作:
/**
* Whether or not this {@link RequestHandler} can handle a request with the given {@link Request}.
*/
//判断某子类是否有能力处理当前请求
public abstract boolean canHandleRequest(Request data);
/**
* Loads an image for the given {@link Request}.
*
* @param request the data from which the image should be resolved.
* @param networkPolicy the {@link NetworkPolicy} for this request.
*
*/
//在canHandleRequest方法返回true的时候,就用该RequestHandler的实现类的load方法来加载图片!
public abstract Result load(Request request, int networkPolicy) throws IOException;
简单分析一下
Picasso怎么处理网络Reqeust的:NetworkRequestHandler
//判断当前请求的图片资源是否来自于服务器或者网络
public boolean canHandleRequest(Request data) {
String scheme = data.uri.getScheme();
return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
}
//NetWorkReqesutHandler对load的处理
public Result load(Request request, int networkPolicy) throws IOException {
//下载图片资源,返回一个Response对象
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;
}
。。。此处有省略代码。。。
return new Result(is, loadedFrom);
}
Picasso怎么处理ResourceReqeust的:ResourceRequestHandler
@Override
public boolean canHandleRequest(Request data) {
//若资源id不为0,则判断为为Resource,能加载
if (data.resourceId != 0) {
return true;
}
//判断文件scheme是否为 RESOURCE
return SCHEME_ANDROID_RESOURCE.equals(data.uri.getScheme());
}
@Override
public Result load(Request request, int networkPolicy) throws IOException {
//加载Resource文件(R文件引用)
Resources res = Utils.getResources(context, request);
int id = Utils.getResourceId(res, request);
return new Result(decodeResource(res, id, request), DISK);
}
Picasso怎么处理FileReqeust的:FileRequestHandler
@Override public boolean canHandleRequest(Request data) {
//判断文件scheme是否为FILE
return SCHEME_FILE.equals(data.uri.getScheme());
}
@Override public Result load(Request request, int networkPolicy) throws IOException {
return new Result(null, getInputStream(request), DISK, getFileExifRotation(request.uri));
}
其他文件来源均类似
注意此时Picasso并没有立即调用RequestHandler对象的load方法进行处理,而是继续调用 service.submit(hunter);方法来在线程池中对BitmapHunter这个Runnable进行处理,所以我们不难猜测出来在run方法中必然调用了RequestHandler的load方法!
那么就继续追踪BimapHunter的run方法发现其调用了hunt()方法,那么hunt()方法是由做了什么
//BitmapHunter的run方法
public void run() {
result = hunt();//获取执行结果
}
//注意该方法返回了一个bitmap
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
//获取读取内存代码省略
data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
//此处真是调用了load方法进行处理
RequestHandler.Result result = requestH andler.load(data, networkPolicy);
....此处省略大量代码....
return bitmap;
}
在hunt()方法中正式调用了forReqeuset过滤的ReqesutHandler对象的load方法完成了核心业务功能!
上面简单的介绍了一下Picasso的工作流程,,希望能对大家有所帮助~~~