Picasso源码初探
Picasso是Square基于Android平台的开源框架,提供图片加载和缓存功能,官网这样描述:
A powerful image downloading and caching library for Android
本文基于Picasso 2.6.0-SNAPSHOT版本代码,粗略分析其运行原理。相关网站:Picasso主页,github地址
Picasso框架一览
Picasso使用非常方便,如下:
Picasso.with(context).load("http://sample/image.png").into(imageView);
Picasso框架使用异步加载的方式加载图片。Picasso类是框架主要入口,其with方法调用Picasso.Builder创建Picasso对象。Builder的build方法中初始化了框架中主要的部件,如下:
public Picasso build() {
Context context = this.context;
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context); // 设置Downloader
}
if (cache == null) {
cache = new LruCache(context); //设置缓存
}
if (service == null) {
service = new PicassoExecutorService(); //ExecutorService
}
if (transformer == null) {
transformer = RequestTransformer.IDENTITY; //Request转换器
}
Stats stats = new Stats(cache); // cache状态
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats); // Dispatcher
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
进行网络下载时,若存在okhttp库,Downloader为OkHttp3Downloader,否则使用HttpUrlConnection。Cache默认使用LruCache。ExecutorService初始化为PicassoExecutorService,PicassoExecutorService继承ThreadPoolExecutor,根据当前安卓平台网络环境设置线程数目。Dispatcher是请求的分发器,负责异步操作的派发和线程的调度。
Picasso的load方法返回RequestCreator对象。RequestCreator封装Request请求生成过程,并提供一系列方法进行图片操作设置,如resize,fit,rotate。调用into方法后开始加载图片操作。图片加载流程如下:
Dispatcher
Dispatcher在框架中处于枢纽位置,Picasso对象调用dispatchSubmit方法发出请求,Dispatcher存在DispatcherThread对象引用,通过的dispatcherHandler发送消息到dispatcherThread线程,然后调用ExecuteService.submit异步处理请求,并通过dispatcherHandler和回调返回结果。其performSubmit方法实现请求提交过程,如下:
void performSubmit(Action action, boolean dismissFailed) {
.... //忽略代码
BitmapHunter hunter = hunterMap.get(action.getKey()); // 判断action是否存在
if (hunter != null) {
hunter.attach(action);
return;
}
// 判断ExecutorSevice是否停止
if (service.isShutdown()) {
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
}
return;
}
// 生成BitmapHunter对象
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
// 提交hunt对象在ExecutorSevice异步运行
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());
}
}
BitmapHunter实现了加载图片和转换图片的操作。BitmapHunter实现Runnable接口,其主要逻辑实现在于hunt方法,hunt代码如下:
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
// 读取cache
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.load加载图片
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
if (result != null) {
loadedFrom = result.getLoadedFrom();
exifOrientation = result.getExifOrientation();
bitmap = result.getBitmap();
// 从InputStream获取bitmap
if (bitmap == null) {
InputStream is = result.getStream();
try {
bitmap = decodeStream(is, data);
} finally {
Utils.closeQuietly(is);
}
}
}
if (bitmap != null) {
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId());
}
stats.dispatchBitmapDecoded(bitmap);
// 图片转换
if (data.needsTransformation() || exifOrientation != 0) {
synchronized (DECODE_LOCK) {
if (data.needsMatrixTransform() || exifOrientation != 0) {
bitmap = transformResult(data, bitmap, exifOrientation);
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;
}
BitmapHunter调用RequestHandler.load方法加载图片,在图片加载成功后调用transformResult方法进行图片变换。
RequestHandler
Picasso支持从本地、网络、content provider等加载图片,由RequestHandler实现对不同图片加载请求类型的支持。Picasso类中存在RequestHandler列表成员对象,构造函数中初始化列表。初始化代码如下:
int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
List<RequestHandler> allRequestHandlers =
new ArrayList<RequestHandler>(builtInHandlers + extraCount);
allRequestHandlers.add(new ResourceRequestHandler(context));
if (extraRequestHandlers != null) {
allRequestHandlers.addAll(extraRequestHandlers);
}
allRequestHandlers.add(new ContactsPhotoRequestHandler(context)); // 通信录图片
allRequestHandlers.add(new MediaStoreRequestHandler(context)); //MediaStore
allRequestHandlers.add(new ContentStreamRequestHandler(context)); // content provider
allRequestHandlers.add(new AssetRequestHandler(context)); // assert文件夹
allRequestHandlers.add(new FileRequestHandler(context)); // file文件
allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats)); //网络图片
requestHandlers = Collections.unmodifiableList(allRequestHandlers);
RequestHandler通过重写canHandleRequest方法实现对不同类型支持。在NetworkRequestHandler中,调用Downloader的load方法实现网络加载。
Downloader
Downloader是实际的网络下载类,当okhttp库存在时,使用okhttp库做网络下载,否则使用HttpURLConnection。
Cache/Stats
Cache表示图片缓存接口,默认使用LruCache,实现了近期最少使用算法。Stats用于记录缓存情况和计算图片加载情况,包含cacheHits,cacheMisses,totalDownloadSize等属性。
总结
Picasso是优秀的开源框架,实现了图片加载、变换和缓存操作。