您的位置:首页 > 其它

Picasso 源码流程分析

2016-05-15 20:29 288 查看

前言

Picasso 是非常好用的图像加载框架,它清晰的图片加载流程:with->load->into,即使用当前上下文加载指定路径的图片资源,然后显示在 ImageView 中。所以,分析 Picasso 的源码,可以从它的流程着手。

用法回顾:Picasso.with(context).load(uri).into(mImageView);

整体流程

一. with() 创建必备的对象

创建 Downloader 对象,Utils.createDownloader(context);

创建 LruCache 对象

创建 ExecuteorService 线程池对象

创建 RequestTransfomer 对象

实例化 Diaptcher 对象,开启异步下载,开启缓存

实例化 Picasso 对象:执行监听、图形转换、调试模式等

二. load()

创建 RequestCreator对象

三. into()

创建 Request、Action 对象

交给 Dispatcher 执行

创建 BitmaHunter (用户捕获图片)

with 过程

首先在 with 方法内部使用 Builder 创建了单例的 Picasso 对象,而且这种单例是加上 volatile 关键字+双重检查加锁+ 懒汉式,懒汉式特点是:线程安全、高效、延迟加载(节约内存)。使用 volatile 关键字可以禁止 CPU 重排序优化、单例的修改对所有线程可见。此种写法,在 Android 系统不建议使用枚举的前提下,不失为一种较优解。

static volatile Picasso singleton = null;

public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}


接着,在 build 中初始化各种对象

/** Create the {@link Picasso} instance. */
public Picasso build() {
Context context = this.context;

// 创建默认下载器
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
// 默认的内存缓存算法(通过Lru 最近最少原则,进行内存的管理)
if (cache == null) {
cache = new LruCache(context);
}
// 创建Picasso自有的线程池对象(通过多线程加载网络图片)
if (service == null) {
service = new PicassoExecutorService();
}
// 初始化自定义的Transmform对象(用户对图片的自定义转换)
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}

// 创建了一个统计类
Stats stats = new Stats(cache);

// 初始化调度者(需要传递:上下文、线程池、handler,下载器,缓存目录和统计类)
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);

// 最终利用 builder 对象的各种属性、对象,创建Picasso对象(需要传递:上下文、调度者、缓存目录、监听器、handler等,默认图片参数、是否开启调试标志、是否开启日志标志)
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
}


Picasso 是如何初始化默认的 Downloader 的?

答案是:在 createDefaultDownloader 中,首先通过反射查找是否存在okHttpClient 对象,如果存在则使用 okHttpDownloader,否则使用 UrlConnectionDownloader

static Downloader createDefaultDownloader(Context context) {
try {
Class.forName("com.squareup.okhttp.OkHttpClient");
return OkHttpLoaderCreator.create(context);
} catch (ClassNotFoundException ignored) {
}
return new UrlConnectionDownloader(context);
}


在 Picasso 类中,还能清晰的看到定义调试模式的颜色

(当设置 Picasso 属性 indicatorsEnabled(true),加载后的 Bitmap 左上角会根据不同颜色指示图片来源):

来自内存:绿色

来自硬盘:蓝色

来自红色:网络

/** Describes where the image was loaded from. */
public enum LoadedFrom {
MEMORY(Color.GREEN),
DISK(Color.BLUE),
NETWORK(Color.RED);

final int debugColor;

private LoadedFrom(int debugColor) {
this.debugColor = debugColor;
}
}


load 过程

load(Uri) 返回的 RequestCreator 是一个请求创建者,可以对其设置各种属性。Picasso.load() 方法可以加载:File、resId、String 以及其他Uri类型的地址,除了 resId 单独作为参数交给 Request.Builder,其他的都被转换成了 Uri。

RequestCreateor构造方法主要的作用:

传入了 Picasso 对象

使用 Request.Builder 创建了 Request 对象,后续对ReqestCreateor 的设置都作用在 Reqest 对象中

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);
}


RequestCreator 提供的各种设置方法:

public RequestCreator noPlaceholder() 不需要加载前显示的图片
public RequestCreator placeholder(int placeholderResId) 设置加载前显示的图片
public RequestCreator error(int errorResId) 加载出错时显示的图片
public RequestCreator tag(Object tag) 设置全局tag,以便对加载过程进行暂停、取消、继续的操作
public RequestCreator fit() 设置图片的显示模式为:填充ImageView控件宽高
public RequestCreator resize(int targetWidth, int targetHeight) 重新调整图片的大小
public RequestCreator centerCrop() 和 resize 方法配合使用,以resize前的尺寸作为参考,按比例修改图片尺寸
public RequestCreator centerInside() 和 resize 方法配合使用,以resize后的尺寸作为参考,将图片强制缩放到resize


into 过程

如果说 with、load 是 Picasso 图像加载的前戏, into 就是高潮:下载(hunt)、解析(decodeStream)。这个过程分散在以下几个区域。让我们一一解析。

一、RequestCreater类中

1 . checkMain 检查当前线程是否是主线程

为啥要检查是否在主线程,因为通过分析可以发现,BitmapHunter 类下载完图片、解析完成后,会通过 handler message 机制向主线程发送消息,所以必须检查是否在主线程

2 . 设置图片的占位符、调整图片大小、图片裁切等

就是之前介绍的 RequestCreator 中的系列设置方法

3 . 创建 Request 对象

4 . 从内存中获取图片,获取到则直接返回

5 . 创建 ImageViewAction 对象

Picasso.enqueueActionSubmit(action) 提交请求

将上面的action对象提交到Picasso中了

二、Picasso 类中

1 . Picasso中submit(action);

2 . dispatcher.dispatcherSubmit(action);

上面说到,RequestCreator 对象中通过Picasso.enqueueActionSubmit(action) 将 action 提交到 Picasso 中,而 Picasso 中通过 dispatcher.dispatcherSubmit(action) 又将对象放入调度者中

三、Dispatcher类中

1 . handler.sendMessage()

2 . handler 类回调 handleMessage

3 . 创建 bitmaoHunter

上面说到,Picasso 中通过 dispatch.dispatchSubmit(action) 方法,将 action 提交到 dispatcher 调度者中,而调度者通过一个 handler message,将 action又重新发到 Picasso 中,最后在 Picasso 的 handler 的 handlerMessage 中创建了 BitmapHunter 对象。

四、BitmapHunter

1 . hunt 获取图片

2 . decodeStream 对图片进行解析

上面说到,Picasso 的handler 的 handleMessgae 中创建了BitmapHunter,而 BitmapHunter 将具体通过线程池下载和解析图片(hunt and decodeStream)

另外,BitmapHunter 实现了Runnable接口(就是一个会被添加到线程池ExecutorService 的一个任务),所有的加载、解析图片的过程都发生在子线程

hunt 的全过程:

Bitmap hunt() throws IOException {
Bitmap bitmap = null;

// 首先从内存缓存中加载图片(根据在RequestCreator中设置的memoryPolicy)
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;
}
}

// 网络加载图片(根据在RequestCreator中设定的NetworkPolicy)
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 {
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) {
// 在这里根据用户自定义的图形转换规则,对Bitmap做进一步处理
stats.dispatchBitmapTransformed(bitmap);
}
}
}

// 加载、解析后返回被处理的btimap。至此,整个Picasso的异步加载过程完成
return bitmap;
}


至此,整个过程算是完成了

附录

参考:

1 . Android图片加载库Picasso源码分析

2 . picasso-强大的Android图片下载缓存库
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: