Android Picasso 请求构建模块深度剖析
本人掘金号,欢迎点击关注:掘金号地址
本人公众号,欢迎点击关注:公众号地址
一、引言
在 Android 开发中,图片加载是一个常见且重要的功能。Picasso 作为一款广受欢迎的图片加载库,以其简洁的 API 和高效的性能受到开发者的青睐。请求构建模块是 Picasso 的核心部分之一,它负责创建和配置图片加载请求。本文将从源码级别深入分析 Android Picasso 的请求构建模块,详细阐述其工作原理和实现细节。
二、Picasso 请求构建模块概述
2.1 请求构建模块的作用
请求构建模块的主要作用是允许开发者通过一系列的方法调用,逐步构建一个完整的图片加载请求。这些请求包含了图片的来源、目标视图、加载选项(如缓存策略、图片变换等)等信息,最终将这些信息传递给 Picasso 的其他模块进行处理。
2.2 请求构建模块的主要类
在 Picasso 中,请求构建模块主要涉及到以下几个类:
RequestCreator
:这是请求构建的核心类,开发者通过调用该类的方法来配置请求。Request
:表示一个完整的图片加载请求,包含了请求的所有信息。RequestHandler
:用于处理具体的请求,根据请求的类型(如网络请求、本地文件请求等)进行相应的处理。
三、RequestCreator 类分析
3.1 RequestCreator 类的初始化
RequestCreator
类的初始化通常是通过 Picasso
类的 load
方法来完成的。以下是 Picasso
类的 load
方法的源码:
java
// Picasso.java
/**
* 从指定的 URI 加载图片
* @param uri 图片的 URI
* @return RequestCreator 对象,用于进一步配置请求
*/
public RequestCreator load(Uri uri) {
// 检查 URI 是否为空
if (uri == null) {
return new RequestCreator(this, null, 0);
}
// 调用构造函数创建 RequestCreator 对象
return new RequestCreator(this, uri, 0);
}
/**
* 从指定的 URL 加载图片
* @param url 图片的 URL
* @return RequestCreator 对象,用于进一步配置请求
*/
public RequestCreator load(String url) {
// 检查 URL 是否为空
if (url == null) {
return new RequestCreator(this, null, 0);
}
// 处理以 "file:" 开头的 URL,将其转换为 URI
if (url.trim().length() == 0) {
throw new IllegalArgumentException("Path must not be empty.");
}
return load(Uri.parse(url));
}
/**
* 从指定的资源 ID 加载图片
* @param resourceId 图片的资源 ID
* @return RequestCreator 对象,用于进一步配置请求
*/
public RequestCreator load(int resourceId) {
// 检查资源 ID 是否为 0
if (resourceId == 0) {
throw new IllegalArgumentException("Resource ID must not be zero.");
}
// 调用构造函数创建 RequestCreator 对象
return new RequestCreator(this, resourceId);
}
从上述源码可以看出,Picasso
类的 load
方法根据不同的参数类型(Uri
、String
或 int
)创建了 RequestCreator
对象。下面是 RequestCreator
类的构造函数源码:
java
// RequestCreator.java
/**
* 构造函数,用于从 URI 或资源 ID 初始化 RequestCreator
* @param picasso Picasso 实例
* @param uri 图片的 URI
* @param resourceId 图片的资源 ID
*/
RequestCreator(Picasso picasso, Uri uri, int resourceId) {
// 检查 Picasso 实例是否为空
if (picasso == null) {
throw new IllegalArgumentException("Picasso must not be null.");
}
this.picasso = picasso;
this.data = new Request.Builder();
// 根据传入的参数设置请求的 URI 或资源 ID
if (uri != null) {
data.uri(uri);
} else if (resourceId != 0) {
data.resourceId(resourceId);
}
}
在 RequestCreator
的构造函数中,首先检查 Picasso
实例是否为空,然后创建了一个 Request.Builder
对象,并根据传入的 Uri
或 resourceId
设置请求的数据源。
3.2 RequestCreator 类的配置方法
RequestCreator
类提供了一系列的配置方法,用于设置请求的各种选项。以下是一些常见的配置方法及其源码分析:
3.2.1 设置占位图
java
// RequestCreator.java
/**
* 设置在图片加载过程中显示的占位图
* @param placeholderResId 占位图的资源 ID
* @return 当前 RequestCreator 对象,用于链式调用
*/
public RequestCreator placeholder(int placeholderResId) {
// 检查占位图资源 ID 是否为 0
if (placeholderResId == 0) {
throw new IllegalArgumentException("Placeholder image resource invalid.");
}
// 设置占位图资源 ID
this.placeholderResId = placeholderResId;
return this;
}
/**
* 设置在图片加载过程中显示的占位图
* @param placeholderDrawable 占位图的 Drawable 对象
* @return 当前 RequestCreator 对象,用于链式调用
*/
public RequestCreator placeholder(Drawable placeholderDrawable) {
// 检查占位图 Drawable 对象是否为空
if (placeholderDrawable == null) {
throw new IllegalArgumentException("Placeholder image may not be null.");
}
// 设置占位图 Drawable 对象
this.placeholderDrawable = placeholderDrawable;
return this;
}
这两个方法分别用于设置占位图的资源 ID 或 Drawable
对象。通过返回 this
,可以实现链式调用,方便开发者进行连续的配置。
3.2.2 设置错误图
java
// RequestCreator.java
/**
* 设置在图片加载失败时显示的错误图
* @param errorResId 错误图的资源 ID
* @return 当前 RequestCreator 对象,用于链式调用
*/
public RequestCreator error(int errorResId) {
// 检查错误图资源 ID 是否为 0
if (errorResId == 0) {
throw new IllegalArgumentException("Error image resource invalid.");
}
// 设置错误图资源 ID
this.errorResId = errorResId;
return this;
}
/**
* 设置在图片加载失败时显示的错误图
* @param errorDrawable 错误图的 Drawable 对象
* @return 当前 RequestCreator 对象,用于链式调用
*/
public RequestCreator error(Drawable errorDrawable) {
// 检查错误图 Drawable 对象是否为空
if (errorDrawable == null) {
throw new IllegalArgumentException("Error image may not be null.");
}
// 设置错误图 Drawable 对象
this.errorDrawable = errorDrawable;
return this;
}
这两个方法的作用与设置占位图的方法类似,只是用于设置图片加载失败时显示的错误图。
3.2.3 设置图片变换
java
// RequestCreator.java
/**
* 添加一个图片变换操作
* @param transformation 图片变换对象
* @return 当前 RequestCreator 对象,用于链式调用
*/
public RequestCreator transform(Transformation transformation) {
// 检查变换对象是否为空
if (transformation == null) {
throw new IllegalArgumentException("Transformation must not be null.");
}
// 如果变换列表为空,则创建一个新的列表
if (transformations == null) {
transformations = new ArrayList<>(2);
}
// 将变换对象添加到变换列表中
transformations.add(transformation);
return this;
}
/**
* 添加多个图片变换操作
* @param transformations 图片变换对象列表
* @return 当前 RequestCreator 对象,用于链式调用
*/
public RequestCreator transform(List<? extends Transformation> transformations) {
// 检查变换列表是否为空
if (transformations == null) {
throw new IllegalArgumentException("Transformation list must not be null.");
}
// 如果变换列表为空,则创建一个新的列表
if (this.transformations == null) {
this.transformations = new ArrayList<>(transformations.size());
}
// 将所有变换对象添加到变换列表中
this.transformations.addAll(transformations);
return this;
}
这两个方法用于添加图片变换操作。Transformation
是一个接口,开发者可以实现该接口来定义自己的图片变换逻辑。
3.2.4 设置缓存策略
java
// RequestCreator.java
/**
* 设置缓存策略
* @param memoryPolicy 内存缓存策略
* @param diskPolicy 磁盘缓存策略
* @return 当前 RequestCreator 对象,用于链式调用
*/
public RequestCreator memoryPolicy(MemoryPolicy... memoryPolicy) {
// 检查内存缓存策略数组是否为空
if (memoryPolicy == null) {
throw new IllegalArgumentException("Memory policy array must not be null.");
}
// 检查内存缓存策略数组是否为空
if (memoryPolicy.length == 0) {
throw new IllegalArgumentException("At least one memory policy must be specified.");
}
// 设置内存缓存策略
this.memoryPolicy = memoryPolicy;
return this;
}
/**
* 设置磁盘缓存策略
* @param diskPolicy 磁盘缓存策略
* @return 当前 RequestCreator 对象,用于链式调用
*/
public RequestCreator diskPolicy(DiskPolicy... diskPolicy) {
// 检查磁盘缓存策略数组是否为空
if (diskPolicy == null) {
throw new IllegalArgumentException("Disk policy array must not be null.");
}
// 检查磁盘缓存策略数组是否为空
if (diskPolicy.length == 0) {
throw new IllegalArgumentException("At least one disk policy must be specified.");
}
// 设置磁盘缓存策略
this.diskPolicy = diskPolicy;
return this;
}
这两个方法分别用于设置内存缓存策略和磁盘缓存策略。MemoryPolicy
和 DiskPolicy
是枚举类型,定义了不同的缓存策略选项。
3.2.5 设置优先级
java
// RequestCreator.java
/**
* 设置请求的优先级
* @param priority 请求的优先级
* @return 当前 RequestCreator 对象,用于链式调用
*/
public RequestCreator priority(Priority priority) {
// 检查优先级对象是否为空
if (priority == null) {
throw new IllegalArgumentException("Priority must not be null.");
}
// 设置请求的优先级
this.priority = priority;
return this;
}
该方法用于设置请求的优先级,Priority
是一个枚举类型,定义了不同的优先级选项。
3.3 RequestCreator 类的请求执行方法
RequestCreator
类提供了几个方法用于执行图片加载请求,以下是其中一个常见的方法:
java
// RequestCreator.java
/**
* 将图片加载到指定的 ImageView 中
* @param target 目标 ImageView
*/
public void into(ImageView target) {
// 检查目标 ImageView 是否为空
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
// 检查是否有有效的请求数据
if (!data.hasImage()) {
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}
// 创建一个新的 Request 对象
Request request = createRequest(started);
// 获取请求的稳定键
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);
return;
}
}
// 设置占位图
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
// 创建一个 Action 对象
Action action =
new ImageViewAction(picasso, target, request, memoryPolicy, diskPolicy, priority,
errorResId, errorDrawable, requestKey, tag, noFade);
// 将 Action 对象提交给 Picasso 进行处理
picasso.enqueueAndSubmit(action);
}
在 into
方法中,首先检查目标 ImageView
是否为空,然后创建一个 Request
对象,并检查是否有缓存的图片。如果有缓存的图片,则直接显示;否则,设置占位图,并创建一个 Action
对象,将其提交给 Picasso
进行处理。
四、Request 类分析
4.1 Request 类的结构
Request
类表示一个完整的图片加载请求,包含了请求的所有信息。以下是 Request
类的部分源码:
java
// Request.java
public final class Request {
// 图片的 URI
public final Uri uri;
// 图片的资源 ID
public final int resourceId;
// 目标图片的宽度
public final int targetWidth;
// 目标图片的高度
public final int targetHeight;
// 是否需要调整图片大小
public final boolean hasSize;
// 是否需要旋转图片
public final boolean rotateEnabled;
// 旋转角度
public final float rotationDegrees;
// 旋转中心点的 X 坐标
public final float rotationPivotX;
// 旋转中心点的 Y 坐标
public final float rotationPivotY;
// 是否有旋转中心点
public final boolean hasRotationPivot;
// 图片变换列表
public final List<Transformation> transformations;
// 请求的稳定键
public final String stableKey;
// 缓存键
public final String cacheKey;
// 请求的优先级
public final Priority priority;
private Request(Uri uri, int resourceId, List<Transformation> transformations, int targetWidth,
int targetHeight, boolean hasSize, float rotationDegrees, float rotationPivotX,
float rotationPivotY, boolean hasRotationPivot, String stableKey, Priority priority) {
this.uri = uri;
this.resourceId = resourceId;
this.transformations = transformations;
this.targetWidth = targetWidth;
this.targetHeight = targetHeight;
this.hasSize = hasSize;
this.rotationDegrees = rotationDegrees;
this.rotationPivotX = rotationPivotX;
this.rotationPivotY = rotationPivotY;
this.hasRotationPivot = hasRotationPivot;
this.stableKey = stableKey;
this.cacheKey = createCacheKey();
this.priority = priority;
}
// 其他方法...
}
从上述源码可以看出,Request
类包含了图片的 URI、资源 ID、目标大小、旋转信息、图片变换列表、请求的稳定键、缓存键和优先级等信息。
4.2 Request.Builder 类
Request.Builder
类用于构建 Request
对象,它提供了一系列的方法来设置请求的各个属性。以下是 Request.Builder
类的部分源码:
java
// Request.java
public static final class Builder {
// 图片的 URI
private Uri uri;
// 图片的资源 ID
private int resourceId;
// 目标图片的宽度
private int targetWidth;
// 目标图片的高度
private int targetHeight;
// 是否需要调整图片大小
private boolean hasSize;
// 是否需要旋转图片
private boolean rotateEnabled;
// 旋转角度
private float rotationDegrees;
// 旋转中心点的 X 坐标
private float rotationPivotX;
// 旋转中心点的 Y 坐标
private float rotationPivotY;
// 是否有旋转中心点
private boolean hasRotationPivot;
// 图片变换列表
private List<Transformation> transformations;
// 请求的稳定键
private String stableKey;
// 请求的优先级
private Priority priority;
/**
* 构造函数,初始化请求构建器
*/
public Builder() {
// 初始化优先级为默认优先级
this.priority = Priority.NORMAL;
}
/**
* 设置图片的 URI
* @param uri 图片的 URI
* @return 当前 Builder 对象,用于链式调用
*/
public Builder uri(Uri uri) {
this.uri = uri;
return this;
}
/**
* 设置图片的资源 ID
* @param resourceId 图片的资源 ID
* @return 当前 Builder 对象,用于链式调用
*/
public Builder resourceId(int resourceId) {
this.resourceId = resourceId;
return this;
}
// 其他设置方法...
/**
* 构建 Request 对象
* @return 构建好的 Request 对象
*/
public Request build() {
// 检查是否有有效的图片数据源
if (uri == null && resourceId == 0) {
throw new IllegalStateException("Either a URI or a resource ID must be specified.");
}
// 检查图片变换列表是否为空,如果为空则创建一个空列表
if (transformations != null && transformations.isEmpty()) {
transformations = null;
}
// 创建并返回 Request 对象
return new Request(uri, resourceId, transformations, targetWidth, targetHeight, hasSize,
rotationDegrees, rotationPivotX, rotationPivotY, hasRotationPivot, stableKey,
priority);
}
}
Request.Builder
类提供了一系列的设置方法,用于设置请求的各个属性。最后,通过调用 build
方法构建一个 Request
对象。
五、RequestHandler 类分析
5.1 RequestHandler 类的作用
RequestHandler
类用于处理具体的图片加载请求,根据请求的类型(如网络请求、本地文件请求等)进行相应的处理。RequestHandler
是一个抽象类,具体的请求处理逻辑由其子类实现。
5.2 常见的 RequestHandler 子类
5.2.1 NetworkRequestHandler
NetworkRequestHandler
用于处理网络图片请求,以下是其部分源码:
java
// NetworkRequestHandler.java
public class NetworkRequestHandler extends RequestHandler {
// 网络下载器
private final Downloader downloader;
// 统计信息
private final Stats stats;
/**
* 构造函数,初始化网络请求处理器
* @param downloader 网络下载器
* @param stats 统计信息
*/
public NetworkRequestHandler(Downloader downloader, Stats stats) {
this.downloader = downloader;
this.stats = stats;
}
@Override
public boolean canHandleRequest(Request data) {
// 检查请求的 URI 是否以 "http" 或 "https" 开头
String scheme = data.uri.getScheme();
return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
}
@Override
public Result load(Request request, int networkPolicy) throws IOException {
// 记录开始时间
long started = System.nanoTime();
// 获取网络响应
Response response = downloader.load(request.uri, networkPolicy);
// 检查响应是否为空
if (response == null) {
return null;
}
// 检查是否有缓存的响应
boolean fromCache = response.cached;
// 获取输入流
InputStream is = response.getInputStream();
if (is == null) {
return null;
}
// 记录结束时间
long size = is instanceof ContentLengthInputStream
? ((ContentLengthInputStream) is).getContentLength() : -1;
// 记录统计信息
stats.dispatchDownloadFinished(System.nanoTime() - started, size, fromCache);
// 返回加载结果
return new Result(is, fromCache ? DISK : NETWORK);
}
}
NetworkRequestHandler
的 canHandleRequest
方法用于判断是否可以处理某个请求,load
方法用于实际加载网络图片。
5.2.2 ResourceRequestHandler
ResourceRequestHandler
用于处理本地资源图片请求,以下是其部分源码:
java
// ResourceRequestHandler.java
public class ResourceRequestHandler extends RequestHandler {
// 上下文对象
private final Context context;
/**
* 构造函数,初始化资源请求处理器
* @param context 上下文对象
*/
public ResourceRequestHandler(Context context) {
this.context = context;
}
@Override
public boolean canHandleRequest(Request data) {
// 检查请求的 URI 是否以 "android.resource" 开头或是否有有效的资源 ID
return (SCHEME_ANDROID_RESOURCE.equals(data.uri.getScheme()) || data.resourceId != 0);
}
@Override
public Result load(Request request, int networkPolicy) throws IOException {
// 获取资源 ID
int id = request.resourceId;
if (id == 0) {
id = ResourceUtils.getResourceId(context, request.uri);
}
// 打开资源输入流
InputStream is = context.getResources().openRawResource(id);
// 返回加载结果
return new Result(is, DISK);
}
}
ResourceRequestHandler
的 canHandleRequest
方法用于判断是否可以处理某个请求,load
方法用于实际加载本地资源图片。
六、请求构建模块的工作流程总结
6.1 初始化 RequestCreator
开发者通过 Picasso
类的 load
方法创建一个 RequestCreator
对象,并传入图片的 URI、URL 或资源 ID。
6.2 配置请求选项
开发者可以调用 RequestCreator
类的各种配置方法,如设置占位图、错误图、图片变换、缓存策略、优先级等。
6.3 执行请求
开发者调用 RequestCreator
类的 into
方法,将图片加载到指定的 ImageView
中。在 into
方法中,会创建一个 Request
对象,并检查是否有缓存的图片。如果有缓存的图片,则直接显示;否则,设置占位图,并创建一个 Action
对象,将其提交给 Picasso
进行处理。
6.4 请求处理
Picasso
会根据请求的类型选择合适的 RequestHandler
进行处理。RequestHandler
的 canHandleRequest
方法用于判断是否可以处理某个请求,load
方法用于实际加载图片。
七、总结与展望
7.1 总结
通过对 Android Picasso 请求构建模块的源码分析,我们深入了解了其工作原理和实现细节。请求构建模块通过 RequestCreator
类提供了简洁的 API,允许开发者通过链式调用的方式配置图片加载请求。Request
类表示一个完整的请求,包含了请求的所有信息。RequestHandler
类负责处理具体的请求,根据请求的类型进行相应的处理。
7.2 展望
未来,Android Picasso 的请求构建模块可以在以下几个方面进行改进和扩展:
- 支持更多的请求配置选项:例如,支持自定义的请求头、超时时间等。
- 优化缓存策略:提供更灵活的缓存策略配置,如根据图片的使用频率进行缓存清理。
- 增强错误处理机制:提供更详细的错误信息,方便开发者进行调试和优化。
- 支持多线程请求处理:提高请求处理的并发性能,加快图片加载速度。
总之,Android Picasso 的请求构建模块是一个非常强大和灵活的模块,通过不断的改进和扩展,它将在 Android 开发中发挥更大的作用。