开源框架剖析-图片加载Picasso

Posted by ZYT on March 8, 2020

开源框架剖析-图片加载Picasso

Picasso是一个强大的图片加载缓存框架

使用流程

Picasso.get()
            .load(path)
            .placeholder(R.drawable.block_canary_icon)
            .error(R.drawable.block_canary_icon)
            .resize(480,480)//
            .centerCrop()
            .rotate(360)
            .priority(Picasso.Priority.HIGH)
            .tag("tage1")//可以用了暂停图片请求
            .memoryPolicy(MemoryPolicy.NO_CACHE)//内存缓存
            .networkPolicy(NetworkPolicy.NO_CACHE)//磁盘缓存
            .into(imageView);

Picasso核心类/方法

Picasso.get()方法分析

主要是基础配置

public static Picasso get() {
        if (singleton == null) {
              synchronized (Picasso.class) {
                    if (singleton == null) {
                          if (PicassoProvider.context == null) {
                            throw new IllegalStateException("context == null");
                          }
                          singleton = new Builder(PicassoProvider.context).build();
                    }
              }
        }
        return singleton;
}

class Builder {
        public Picasso build() {
              Context context = this.context;
        
              if (downloader == null) {
                downloader = new OkHttp3Downloader(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);
        }
} 

class Dispatcher {//重要的分发器类
    Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler, Downloader downloader, Cache cache, Stats stats) {
        this.dispatcherThread = new DispatcherThread();//一个HandlerThread
        this.dispatcherThread.start();
        Utils.flushStackLocalLeaks(dispatcherThread.getLooper());
        this.context = context;
        this.service = service;//线程池,支持自定义
        this.hunterMap = new LinkedHashMap<>();//BitmapHunter实现了Runnable接口,这个才是网络请求核心类,实现了下载,解码,对bitmap进行
        this.failedActions = new WeakHashMap<>();//保存了失败的action
        this.pausedActions = new WeakHashMap<>();//保存了暂停的action
        this.pausedTags = new LinkedHashSet<>();////暂停tag
        ////自己内部的handler,分发请求线程的时候,就是通过这个自己线程内部的handler进行分发
        this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);//子线程handler
        this.downloader = downloader;//下载接口
        this.mainThreadHandler = mainThreadHandler;//主线程handler
        this.cache = cache;
        this.stats = stats;
        this.batch = new ArrayList<>(4);
        this.airplaneMode = Utils.isAirplaneModeOn(this.context);
        this.scansNetworkChanges = hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE);
        this.receiver = new NetworkBroadcastReceiver(this);//网络变化广播
        receiver.register();
  }
}

在此,插入一个HandlerThread(DispatcherThread)的简介

//Picasso的构造方法
Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
      RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
      Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
    this.context = context;
    this.dispatcher = dispatcher;//dispatcher是负责分发请求的
    this.cache = cache;//picasso的缓存机制
    this.listener = listener;
    this.requestTransformer = requestTransformer;
    this.defaultBitmapConfig = defaultBitmapConfig;

    int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
    int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
    //通过不同的requesthandler来处理不同类型的图片
    List<RequestHandler> allRequestHandlers = new ArrayList<>(builtInHandlers + extraCount);

    // 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<>();
    this.targetToDeferredRequestCreator = new WeakHashMap<>();
    this.indicatorsEnabled = indicatorsEnabled;
    this.loggingEnabled = loggingEnabled;
    this.referenceQueue = new ReferenceQueue<>();
    this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
    this.cleanupThread.start();
  }

get()方法就是一个DoubleCheck的单例模式。但他的单例创建是通过Builder方式做的。

与前期版本不同的是Picasso需要的Context是通过PicassoProvider的context获取的,省掉了手动初始化。

Picasso.load()方法分析

最主要的就是创建了RequestCreator对象。Request.Builder能配置更多的参数. 在into之前都是对请求的描述,设置各种属性。然后生成了Request。

Picasso
    public RequestCreator load(@Nullable Uri uri) {
        return new RequestCreator(this, uri, 0);
    }

RequestCreator
    RequestCreator(Picasso picasso, Uri uri, int resourceId) {
    this.picasso = picasso;
    this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}

Picasso.into()方法分析

class RequestCreator
        public void into(ImageView target, Callback callback) {
            ...
            Action action =
                new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
                    errorDrawable, requestKey, tag, callback, noFade);
        
            picasso.enqueueAndSubmit(action);
        }

最后提交的是一个Action,Action也是一个抽象类,会根据我们不同的请求生成不同的action子类,他包含了请求信息,回调接口,picasso的实例。如果是一次要加载多个图片,那么会产生多个request和多个Aciton。

class Picasso
        void enqueueAndSubmit(Action action) {
            submit(action);
        }

        void submit(Action action) {
            dispatcher.dispatchSubmit(action);
        }

接下来sbumit之后的操作,是通过这个Dispatcher分发

class Dispatcher
    //这就是dispatcher类中的submit方法,使用线程自己的handler
    void dispatchSubmit(Action action) {
        handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
    }

消息的处理在DispatcherHandler的handleMessage方法中,又调用的Dispatcher的performSubmit(action)方法

class Dispatcher
    //主要是获取到真正需要处理的bitmaphunter
    void performSubmit(Action action, boolean dismissFailed) {
            ///检测暂停的请求是否包含此次请求,如果包含,将请求保存到map,返回
            if (pausedTags.contains(action.getTag())) {
              pausedActions.put(action.getTarget(), action);
              return;
            }
            //检测已经提交的请求是否包含此次请求,key就是根据url或者resourceid
            //这样的字段生成,如果已经包含了同样的请求,那么直接合并到同一个hunter中去。
            //bitmaphunter实现了runnable接口,这个类是最终请求网路并进行编码生成bitmap的类
            BitmapHunter hunter = hunterMap.get(action.getKey());
            if (hunter != null) {
              hunter.attach(action);
              return;
            }
        
            if (service.isShutdown()) {//shutdown的就不进行了
              return;
            }
        
            //在这里forRequest方法生成一个bitmaphunter
            hunter = forRequest(action.getPicasso(), this, cache, stats, action);
            //获取返回的结果
            hunter.future = service.submit(hunter);
            //保存到map中
            hunterMap.put(action.getKey(), hunter);
            if (dismissFailed) {
              failedActions.remove(action.getTarget());
            }
     }
     
 

forRequest方法的核心在于,根据request的不同,选择匹配的requesthandler。 requestHandler的核心方法是load。加载网络图片用的是NetworkRequestHandler。 对于多个requestHandler采用一个for循环方式的调用,直到返回正确结果才return,可以看做一种责任链。

service执行了submit之后,将会执行bitmaphunter中的run方法。

BitmapHunter
        @Override public void run() {
                  ...
                  result = hunt();
                  if (result == null) {
                        dispatcher.dispatchFailed(this);
                  } else {
                        dispatcher.dispatchComplete(this);
                  }
                  ...
        }
        
        Bitmap hunt() throws IOException {
                Bitmap bitmap = null;
            
                //是否是内存模式读取
                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;
                  }
                }
                
                networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
                RequestHandler.Result result = requestHandler.load(data, networkPolicy);
                if (result != null) {
                  loadedFrom = result.getLoadedFrom();
                  exifOrientation = result.getExifOrientation();
                  bitmap = result.getBitmap();
            
                  // If there was no Bitmap then we need to decode it from the stream.
                  if (bitmap == null) {
                    Source source = result.getSource();
                    try {
                      bitmap = decodeStream(source, data);
                    } finally {
                      try {
                        //noinspection ConstantConditions If bitmap is null then source is guranteed non-null.
                        source.close();
                      } catch (IOException ignored) {
                      }
                    }
                  }
                }
            
                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;
        }

主要是通过hunt中的load方法获取到了需要的bitmap,然后查看各种属性配置,是否需要对bitmap做额外的处理等。 处理完之后,通过dispatcher将结果分发出去。 最终执行到 mainThreadHandler.sendMessage,然后主线程处理完分发过来的消息,整个流程就算结束了。