Android 常用开源框架源码解析 系列 (十一)picasso 图片框架
一、前言
Picasso 强大的图片加载缓存框架
api加载方式和Glide 类似,均是通过链式调用的方式进行调用
1.1、作用
Picasso 管理整个图片加载、转换、缓存等策略
1.2、简单调用:
Picasso .with(this 传入一个单例,上下文).load(“url”/file文件/资源路径) .into()
1.2.1 、一些简单的链式调用参数
.placeholder(R.drawable.xx) //网络未加载完成的时候显示的本地资源图片
.error(R.drawable.xx) //网络加载失败显示的本地资源图片
.resize(480,800) //手动控制图片宽高 ,对整个屏幕图片进行设置像素设置
.centerCrop () //造成图片拉伸扭曲的时候,将整个图片充满ImageView边界,进行居中裁剪边界
.rotate( 度数 0.0坐标开始转 ) //旋转动画效果
.priority() //请求优先级,会影响请求执行顺序,但不是百分之百保证的,只会往高优先级靠拢
.tag(“xxxx”) //允许为每一个请求设置一个tag
ps:如果不设置tag,在list列表请求的时候,会加载所有的图片请求,就会造成Ui卡顿;
而有了tag ,listView就可以进行监听而后进行不同的操作事件
可以在滑动的setOnScrollListener()的监听事件中 :
在onScrollStateChanged()方法中通过判断是否处于IDLE状态,
如果处于IDLE状态就恢复 tag(picasso.resumeTag) 继续加载;
如果是在滑动状态,停止图片加载的请求 picasso.pauseTag
picasson的内存缓存 和 磁盘缓存 默认都会开启的 ;但是不需要总是开启,如果加载大图片再缓存一份就很容易造成OOM
.memoryPolicy(MemoryPolicy.No_CACHE) //picasso 的内存缓存模式
.networkPolicy(MemoryPolicy.No_CACHE) //人为禁掉 磁盘缓存
二、Picasso 源码
2.1、 引入
implementation 'com.squareup.picasso:picasso:2.71828'
or Maven:
<dependency>
<groupId>com.squareup.picasso</groupId>
<artifactId>picasso</artifactId>
<version>2.71828</version>
</dependency>
2.2 代码混淆
If you are using ProGuard you might need to add OkHttp's rules: https://github.com/square/okhttp/#proguard
2.3 源码分析
// 2.3.1 with()函数的进入-基础配置工作,dispatch分发器配置,picasso对象配置
返回一个picasso对象的实例主旨功能 ,Picasso 入口函数
public void picasso() {
Picasso.with(this) //使用单例模式构建保证在使用的时候整个module只有一个picasso存在
.load("url")
.into(imageView);
}
with()://通过双重锁机制 保护线程的安全,通过Builder内部类的build()方法创建 Picasso对象
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}
build():
public Picasso build() {
Context context = this.context;
//实际图片加载的过程
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
——————————————————
ps: 通过反射查找是否有OkHttpClient库,如果有就通过OkHttp库 进行图片下载和加载方式
Class.forName("com.squareup.okhttp.OkHttpClient");
return OkHttpLoaderCreator.create(context);
}else {
//若没有OkHttp库就通过HttpURLConnection进行实现
return new UrlConnectionDownloader(context);
}
——————————————————
//通过LruCache 最近使用内存算法;进行内存缓存 策略,picasso 中指定了手机的内存为15%
——————————————————
ps:LruCache 实现了Lru算法复习;最近使用最少的缓存会被删除;其实现会通过LinkedHashMap进行,每次调用get()、put()会将对象放到链表的尾端,每次put依然会将对象放到链表的尾部。当内存缓存达到最大值的时候,他就会将我们的链表头部的对象移除,以达到Lru算法的目的。
——————————————————
if (cache == null) {
cache = new LruCache(context);
}
//picasso 的线程池Executor
if (service == null) {
service = new PicassoExecutorService();
——————————————————
ps:PicassoExecutorService 继承自 ThreadPoolExecutor
private static final int DEFAULT_THREAD_COUNT = 3;
public ThreadPoolExecutor(int corePoolSize, //核心线程数量,如果没有任务的话,整个核心的corePoolSize也不会终止,除非认为设置需要核心线程
int maximumPoolSize, //线程池中允许的最大的线程数量,线程池数量越大,开销越大
long keepAliveTime, //当我们线程池中的线程线程数目比核心线程多的时候,如果超过了keepAliveTime这个时间,那么多余的线程就会被回收,也就是被移出线程池
TimeUnit unit,
BlockingQueue<Runnable> workQueue, //阻塞队列,是一个线程安全队列
ThreadFactory threadFactory, //用来构造线程池的工厂
RejectedExecutionHandler handler) //任务阻塞的时候线程池的一种处理方式
{
- 当有新任务的时候先观察corePoolSize值
- 然后观察workQueue工作队列是否已经满了
- 最好判断是否有小于线程池的最大值
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
...
}
思考: 线程池中的线程是如何调度的呢?
- 1、如果线程池中线程的数目少于corePoolSize; 这时候线程池会重新创建一个核心线程的,直到核心线程数量达到corePoolSize就不会再创建新核心线程了
- 2、如果线程池中线程的数目大于或是等于corePoolSize,但是工作队列workQueue没有满,那么新的任务依然会放置到这个队列当中,按照先进先出的原则来进行执行
- 3、如果线程池中的线程数目大于等于corePoolSize,并且工作队列workQueue满了,但是总线程数目小于maximumPoolSize,那么可以直接创建一个线程,处理并添加到任务当中
- 4、如果工作队列满了,并且线程池中线程的数目到达了最大数目maximumPoolSize的时候,这时候就需要用到RejectedExecutionHandler这个对象进行处理,默认处理方式是丢弃任务并抛出异常
——————————————————
}
//配置请求转换器,picasso 不进行转换操作所以直接返回原来请求
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}
//新建一个保留缓存状态对象
Stats stats = new Stats(cache);
//主线程到子线程的切换通过 重要的Dispatcher 完成
Dispatcherdispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
//通过单例完成Picasso 对象,该Picasso 对象是加载图片的入口同时
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
2.3.2 、Dispatcher 完成线程切换
通过该类,在内部使用handler机制 ,完成主线程和子线程之间所有的切换。所以非常重要
Dispatcher()的构造方法:
Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
Downloader downloader, Cache cache, Stats stats) {
//1、在构造方法中创建一个子线程,并开启一个子线程 ,执行耗时操作
this.dispatcherThread = new DispatcherThread();
——————————————————
ps:static class DispatcherThread extends HandlerThread,而HandlerThread又继承自Thread本质上是一个线程类;
这个handlerThread会开启一个新的线程,这个线程内部会有一个looper对象,通过looper对象会循环遍历消息队列
在其核心的 run()方法中 :
//通过looper完成looper的创建,以及通过looper的循环方法来构造一个循环的线程。⚠️HandlerThread需要start()方法开启线程
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare(); //完成Looper对象的创建
synchronized (this) {
//利用同步锁锁住当前对象,通过myLooper()函数获取当前looper并赋值
mLooper = Looper.myLooper();
notifyAll();//唤醒等待线程—wait()成对出现
}
Process.setThreadPriority(mPriority);
onLooperPrepared(); //空实现方法
Looper.loop();//开启线程消息循环
mTid = -1;
}
在getLooper()函数内 ,如果线程存活或是looper对象是空,则进入等待wait()状态,表示进入阻塞阶段,直到looper对象被成功创建同时调用了notifyAll()函数后就会唤醒先前的等待线程。
——————————————————
…
//2、初始化两个很重要的handler,一个是主线程中的handler ,一个是子线程的handler
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
——————————————————
ps:在其构造方法内传入一个looper 和一个dispatcher 分发器,这个handler的作用是处理DispatcherThread 这个子线程当中的handler
public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
super(looper);
this.dispatcher = dispatcher;
}
——————————————————
this.downloader = downloader;
this.mainThreadHandler = mainThreadHandler;//主线程handler
——————————————————
ps:在前面的Dispatcherdispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);表达式中传入的第三个参数就是代表主线程中的handler;该Handler是一个static静态的handler对象,并且传入一个主线程的looper对象,为了防止内存泄漏的产生;在这里如果定义成非静态内部类的话,会持有外部累Picasso的引用,导致picasso对象无法在该被回收的时候进行回收。
——————————————————
…
//3、创建一个广播接收者用于接收外部网络环境的变化,内部通过切换子线程的数量完成理论操作
this.receiver = new NetworkBroadcastReceiver(this);//广播接收者,用于监听网络变化等操作
内部核心方法:onReceive(),监听intent 并做出相应的操作
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null) {
return;
}
final String action = intent.getAction();
//针对飞行模式进行判断 并处理
if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
if (!intent.hasExtra(EXTRA_AIRPLANE_STATE)) {
return; // No airplane state, ignore it. Should we query Utils.isAirplaneModeOn?
}
dispatcher.dispatchAirplaneModeChange(intent.getBooleanExtra(EXTRA_AIRPLANE_STATE, false));
//正常情况下,网络变化的处理逻辑
} else if (CONNECTIVITY_ACTION.equals(action)) {
ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);
dispatcher.dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());
}
}
dispatcher.dispatchNetworkStateChange():
//可以发现使用的是handler通过sendMessage()方法进行消息的传递,交给HandleMessage()进行处理
void dispatchNetworkStateChange(NetworkInfo info) {
handler.sendMessage(handler.obtainMessage(NETWORK_STATE_CHANGE, info));
}
在该方法中进行网络变化的业务逻辑:
dispatcher.performNetworkStateChange(info);
void performNetworkStateChange(NetworkInfo info) {
//线程池判断,在adjustThreadCount()中根据不同的网络类型设置线程的数量,根据用户具体的场景和使用需要设置线程的数量,值得自己的代码进行复刻
if (service instanceof PicassoExecutorService) {
((PicassoExecutorService) service).adjustThreadCount(info);
...
}
2.3.3 、NetworkRequestHandler处理
//用来存放requestHandler ,根据具体的需求选择相应的handler
List<RequestHandler> allRequestHandlers =
new ArrayList<RequestHandler>(builtInHandlers + extraCount);
allRequestHandlers.add(new ContentStreamRequestHandler(context));
allRequestHandlers.add(new AssetRequestHandler(context));//用于处理asset 资源图片,如果加载的图片在Aset资源目录下就
allRequestHandlers.add(new FileRequestHandler(context));//用于处理文件当中的图片
allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));//如果图片需要通过网络下载就会通过这个handler进行处理
NetworkRequestHandler() 里的核心方法:如何加载图片请求
@Override
public Result load(Request request, int networkPolicy) throws IOException {
//开启一个downloader下载器 完成下载
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;
}
// Sometimes response content length is zero when requests are being replayed. Haven't found
// root cause to this but retrying the request seems safe to do so.
if (loadedFrom == DISK && response.getContentLength() == 0) {
Utils.closeQuietly(is);
throw new ContentLengthException("Received response with 0 content-length header.");
}
//如果是从网络下载图片,同时获取到的资源长度大于0 ,表示可以调用成功的回调
if (loadedFrom == NETWORK && response.getContentLength() > 0) {
stats.dispatchDownloadFinished(response.getContentLength());
}
return new Result(is, loadedFrom);
}
//可以发现该方法同样是通过handle 发送消息来控制的,但是要注意的是这里的这个handler不是前面的dispatchHandler中的handler了,这里的这个handler是保存在state 类中的StatsHandler,传入的是statsThread.getLooper()的handler
void dispatchDownloadFinished(long size) {
handler.sendMessage(handler.obtainMessage(DOWNLOAD_FINISHED, size));
}
在case 中 通过 performDownloadFinished()方法
stats.performDownloadFinished((Long) msg.obj);
void performDownloadFinished(Long size) {
downloadCount++; //当前下载数量加1
totalDownloadSize += size;//将总的下载文件的大小,加上本次下载文件大小的值
averageDownloadSize = getAverage(downloadCount, totalDownloadSize);//计算平均下载的大小
}
2.3.4 .load(“")
参数类型:
1、Url 地址
2、path文件路径
3、资源ID
//在load()方法中创建了RequestCreator对象,在RequestCreator对象内部又创建了一个RequestBuilder对象,通过Builder对象可以进行链式调用配置更多参数
public RequestCreator load(String path) {
if (path == null) {
//创建图片加载请求
return new RequestCreator(this, null, 0);
ps:RequestCreator 对picasso进行赋值,并创建一个Request的Builder对象,该对象接收3个参数,url、资源布局Id和一个默认的图片配置
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);
}
}
if (path.trim().length() == 0) {
throw new IllegalArgumentException("Path must not be empty.");
}
return load(Uri.parse(path));
}
2.3.5 .into(“")
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
//1、用于检验当前线程是否实在主线程,非主线程抛出异常
checkMain();
if (target == null) { //ImageView对象不能为空
throw new IllegalArgumentException("Target must not be null.");
}
//2、创建Request的Builder 的内部类 data,hasImage()函数用于判断 url 或是资源Id是否为空
if (!data.hasImage()) {
picasso.cancelRequest(target); //3、为空则取消加载图片请求
————————————————————————————
ps:
private void cancelExistingRequest(Object target) {
checkMain();
//Action是Request的封装类,存储了Picasso、request、memoryPolicy、networkPolicy等对象,封装好后交给BitmapHunter这个Runnabel进行开启线程下载
1、targetToAction 是一个map对象,key是imageView而value就是请求包装类action,取消请求就是把有关的target的信息删除掉;
Action action = targetToAction.remove(target);
if (action != null) {
action.cancel();
2、使用了dispatch线程中的handler通过 sendMessage传递取消请求的请求,通过dispatcher的performCancel()进行取消:
dispatcher.dispatchCancel————>performCancel():
//BitmapHunter 是一个Runnable工具 可以开启线程,下载并处理Bitmap以及小量的图片处理工作
BitmapHunterhunter = hunterMap.get(key);
if (hunter != null) {
hunter.detach(action);
if (hunter.cancel()) { //取消BitmapHunter操作
hunterMap.remove(key);
...
}
dispatcher.dispatchCancel(action);
}
if (target instanceof ImageView) {
3、将target转化成Imageview
ImageView targetImageView = (ImageView) target;
DeferredRequestCreator deferredRequestCreator =
targetToDeferredRequestCreator.remove(targetImageView);
//创建一个图片加载请求的时候,还不能获取到当前ImageView的宽和高,这时候就需要创建一个deferredRequestCreator类用来对ImageView的target去进行监听,直到获取到了ImageView的宽和高后就可以重新执行请求创建了。所以说删除请求也需要删除掉deferredRequestCreator绑定的监听事件
if (deferredRequestCreator != null) {//Requestcreator 创造器的包装类
deferredRequestCreator.cancel();
}
}
}
————————————————————————
if (setPlaceholder) {
4、通过setPlaceHolder()函数设置占位符
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}
5、图片加载是否延迟进行判断
if (deferred) {
if (data.hasSize()) {
throw new IllegalStateException("Fit cannot be used with resize.");
}
根据target宽高进行从新的测量
int width = target.getWidth();
int height = target.getHeight();
...
data.resize(width, height);
}
6、创建图片加载请求
Request request = createRequest(started);
ps:内部通过Request request = data.build();创建Request对象并将这个Request对象进行转换
Request transformed = picasso.transformRequest(request);
只有在创建Picasso的时候有关transform的时候就需要进行转换的操作
//将ImageView和创建好的createKey值进行关联,也就是将ImageView和Request关联到一起为后续需求作准备
String requestKey = createKey(request);
7、判断是否从内存缓存中读取数据
if (shouldReadFromMemoryCache(memoryPolicy)) {
8、获取缓存当中的Bitmap
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
9、如果已经从缓存中读取就没必要在从网络中价值图片了所以一定要取消掉这个请求否则会重复加载
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;
}
}
//Action的构造方法中,将ImageView 添加到了Action.RequestWeakReference这个弱引用中,也就是说可以在内存不足的时候会回收掉这个对象
Action action =
new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
10、进行任务的提交
picasso.enqueueAndSubmit(action);
void enqueueAndSubmit(Action action) { //通过请求包装类获取到target 属性
Object target = action.getTarget();
if (target != null && targetToAction.get(target) != action) { //判断target和请求包装Action类是否配套,如果不配套就取消,会将新的action 添加到 argetToAction这个Map当中
// 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); //内部依然是通过handler进行消息的传递 REQUEST_SUBMIT
}
performSubmit():
void performSubmit(Action action, boolean dismissFailed) {
1、对根据Action的标志查询是否已经存在于pausedTags这个set列表当中
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;
}
2、创建BitmapHunter对象,并调用attach()函数完成工作
BitmapHunter hunter = hunterMap.get(action.getKey());
if (hunter != null) {
hunter.attach(action); //actions是在一个arrayList当中,保证了action 不会重复
return;
}
3、判断线程池是否关闭了 ,如果关闭了就打印异常日志
if (service.isShutdown()) {
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
}
return;
}
4、没有关闭线程池的时候,通过forRequest()函数获取到BitmapHunter对象,
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
————————————————————————————————————
ps:通过action、picasso 对象获取到Request对象和requestHandler对象
Request request = action.getRequest();
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
通过for循环遍历所有的handler请求器,获取到不同业务处理的请求业务处理器
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
Action action) {
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
if (requestHandler.canHandleRequest(request)) {
//最终将BitmapHunter 子线程返回给orRequest()方法
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}
————————————————————————————————————
future 对象用来保存结果
hunter.future = service.submit(hunter);
ps:将图片请求放到线程池中进行
@Override
public Future<?> submit(Runnable task) {
将线程task封装成PicassoFutureTask便于控制处理的线程类,并通过execute()函数开启线程
PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);
execute(ftask);
return ftask;
}
hunterMap.put(action.getKey(), hunter);
if (dismissFailed) {
failedActions.remove(action.getTarget());
}
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
}
思考:线程开启后如何执行图片加载?又是在何地方进行进行操作的呢?
private static final class PicassoFutureTask extends FutureTask<BitmapHunter>
implements Comparable<PicassoFutureTask> {
private final BitmapHunter hunter; //在PicassoFutrueTask中持有了BitmapHunter的引用
单个线程如何操作 ——BitmapHunter
BitmapHunter 核心类:run
@Override
public void run() {
try {
updateThreadName(data);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
}
result = hunt();//整个线程的核心方法
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
}
catch (Downloader.ResponseException e) {
...
} finally {
Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
}
}
hunt():
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
1、是否从内存缓存中读取缓存进行判断
if (shouldReadFromMemoryCache(memoryPolicy)) {
2、根据Action的key值从缓存中查找bitmap是否存在
bitmap = cache.get(key);
if (bitmap != null) {
3、bitmap不为空,修改stats的状态
stats.dispatchCacheHit();
loadedFrom = MEMORY;
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
}
return bitmap;
}
}
4、如果缓存不存在就从网络中加载图片
data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
RequestHandler.Result result =requestHandler.load(data, networkPolicy);//requestHandler是属于networkRequestHandler
ps:RequestHandler的 NetworkRequestHandler实现为例:
@Override
public Result load(Request request, int networkPolicy) throws IOException {
Response response = downloader.load(request.uri, request.networkPolicy); //这里的load
有两个实现类OkHttpDownloader和UrlConnectionDownloader,也就是前者通过反射机制查找是否加载了OkHttp网络框架,如果没有使用OkHttp库则直接使用google自己的HttpUrlConnection进行网络加载
OkHttpDownloader 和 UrlConnectionDownloader 对于 load ()方法的区别
a、OkHttpDownloader是如何下载图片的
@Override
public Response load(Uri uri, int networkPolicy) throws IOException {
1、新建CacheControl 对象 用于对缓存进行处理,设置
CacheControl cacheControl = null;
…
2、新建builder对象用来存放硬盘内存缓存的设置
CacheControl.Builder builder = new CacheControl.Builder();
3、判断是否从内存缓存中读取数据
if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {
builder.noCache();
}
4、如果设置不从硬盘缓存中读取数据
if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {
builder.noStore();
}
5、将build方法的返回值给cacheControl,由cacheControl变量设置OkHttp库的设置
cacheControl = builder.build();
6、新建Request对象并为其url赋值
Request.Builder builder = new Request.Builder().url(uri.toString());
if (cacheControl != null) {
设置Okhttp 缓存策略的头部
builder.cacheControl(cacheControl);
}
7、调用OkHttp同步请求 获取response对象
com.squareup.okhttp.Response response = client.newCall(builder.build()).execute();
int responseCode = response.code();
8、针对响应码进行判断 如果大于等于300就关闭响应体
if (responseCode >= 300) {
response.body().close();
throw new ResponseException(responseCode + " " + response.message(), networkPolicy,
responseCode);
}
9、如果小于300就返回response
boolean fromCache = response.cacheResponse() != null;
ResponseBody responseBody = response.body();
return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength());
}
b、UrlConnectionDownloader是如何下载图片的
@Override
public Response load(Uri uri, int networkPolicy) throws IOException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
installCacheIfNeeded(context);
}
1、新建HttpURLConnection 对象
HttpURLConnection connection = openConnection(uri);
connection.setUseCaches(true); //开启缓存功能
2、判断是否仅在离线情况下使用缓存
if (networkPolicy != 0) {
String headerValue;
if (NetworkPolicy.isOfflineOnly(networkPolicy)) {
headerValue = FORCE_CACHE;
} else {
3、判断是否从内存缓存中读取
if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {
不从缓存中读取 直接添加no-cache 标签
builder.append("no-cache");
}
4、判断是否从硬盘缓存中读取数据
if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {
if (builder.length() > 0) {
builder.append(',');
}
不从硬盘缓存中读取数据添加 no-store标签
builder.append("no-store");
}
headerValue = builder.toString();
5、将获取到的所有缓存信息添加到Cache-Control 头部信息中
connection.setRequestProperty("Cache-Control", headerValue);
6、跟OkHttp一样获取responseCode响应码,大于300关闭连接,小于300返回response
int responseCode = connection.getResponseCode();
if (responseCode >= 300) {
connection.disconnect();
throw new ResponseException(responseCode + " " + connection.getResponseMessage(),
networkPolicy, responseCode);
}
long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE));
return new Response(connection.getInputStream(), fromCache, contentLength);
}
dispatcher.dispatchComplete():
void performComplete(BitmapHunter hunter) {
1、首先对缓存策略进行判断,如果开启内测缓存就
if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
将结果保存到cache缓存当中
cache.set(hunter.getKey(), hunter.getResult());
}
2、从map中移除BitmapHunter对应的key值,因为这会儿请求已经完成可以删除对应的BitmapHunter中的数据否则会重复请求
hunterMap.remove(hunter.getKey());
batch(hunter);
——————————————————
ps:batch()
private void batch(BitmapHunter hunter) {
//1、判断bitmapHunter 是否已经取消
if (hunter.isCancelled()) {
return;
}
//2、没有取消就将这个hunter存放在batch 这个list 集合当中
batch.add(hunter);
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
//3、发送一个空消息 给handler进行处理
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
——————————————————
void performBatchComplete() {
//1、首先获取存放BitmapHunter的集合
List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
//2、清理整个batch集合
batch.clear();
//3、给主线程发送消息 处理事件
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
——————————————————
ps:在handleMessage()中遍历BitmapHunter 并使用picasso.complete()函数进行操作
for (int i = 0, n = batch.size(); i < n; i++) {
BitmapHunter hunter = batch.get(i);
hunter.picasso.complete(hunter);
}
…
//根据BitmapHunter 获取Uri信息、异常信息、bitmap信息等
Uri uri = hunter.getData().uri;
Exception exception = hunter.getException();
Bitmap result = hunter.getResult();
LoadedFrom from = hunter.getLoadedFrom();
if (single != null) {
//分发图片请求的包装类Action
deliverAction(result, from, single);
//在deliverAction()方法中 ,当Action没有被取消,以及bitmap不为空的时候,就会调用 action.complete(result, from)完成一些操作;
在complete的实现类ImageViewAction的complete()中,会将target转换成ImageView,在后面调用
PicassoDrawable的setBitmap()方法中使用setImageDrawable(drawable)方法设置图片,最终调用ImageView进行设置图片的操作
}
——————————————————
...
if (hunter.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
}
}