美文网首页
简谈源码-Picasso源码(v_2.71828)

简谈源码-Picasso源码(v_2.71828)

作者: catface | 来源:发表于2018-12-12 15:31 被阅读4次

    Picasso官网

    Picasso
    Picasso.get().load(url).into(iv);
    

    Picasso的常见使用步骤很简单,下面我们概要的看看其实现。

    1. 实例获取get()

    /* Picasso.class */
    @SuppressLint("StaticFieldLeak") static volatile Picasso singleton = null;
    
    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;
    }
    

    使用double checked locking单例模式创建Picasso实例,创建过程中使用建造者Builder。

    1.1 context获取

    通过ContentProvider,在AndroidManifest 中注册PicassoProvider,app启动后调用onCreate()即可获取所需context实例。

    public final class PicassoProvider extends ContentProvider {
    
        @SuppressLint("StaticFieldLeak") static Context context;
    
        @Override public boolean onCreate() {
            context = getContext();
            return true;
        }
    }
    

    1.2 Picasso创建

    使用Builder Pattern建造者模式,将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示,简单点说就是用多个简单的对象一步一步构建成一个复杂的对象。

    /* Picasso.class */
    public Builder(@NonNull Context context) {
        if (context == null) {
            throw new IllegalArgumentException("Context must not be null.");
        }
        this.context = context.getApplicationContext();
    }
    

    具体建造过程如下。

    /* Picasso.class */
    public Picasso build() {
        Context context = this.context;
    
        // 配置下载器,下载网络图片资源(默认使用OkHttp3Downloader)
        if (downloader == null) {
            downloader = new OkHttp3Downloader(context);
        }
    
        // 配置内存缓存,提高图片加载效率(默认使用LruCache[Least Recently Used])
        // 内存缓存大小为RAM的15%;磁盘缓存大小为ROM的2%且范围为5~50mb
        if (cache == null) {
            cache = new LruCache(context);
        }
    
        // 配置线程池
        // 默认3个线程;使用wifi为4个,4G为3个,3G为2个,2G为1个
        if (service == null) {
            service = new PicassoExecutorService();
        }
    
        // 配置请求转换器(默认不做处理,直接返回原请求)
        if (transformer == null) {
            transformer = RequestTransformer.IDENTITY;
        }
    
        // 统计:统计缓存命中数量、图片下载数量等
        Stats stats = new Stats(cache);
    
        // 分发器:由HANDLER分发图片请求、请求完成等等事件
        Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
    
        return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
                defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
    }
    

    至此,Picasso.get()过程完成。

    2. 获取请求load(url)

    即通过请求创建器RequestCreator创建请求Request,通过Request可以执行placeholder(placeholderResId)方法设置占位图,error(errorResId)方法设置错误图,noFade()方法取消图片渐变效果等操作。

    /* Picasso.class */
    public RequestCreator load(@Nullable String path) {
        ...
        return load(Uri.parse(path));
    }
    
    public RequestCreator load(@Nullable Uri uri) {
        return new RequestCreator(this, uri, 0);
    }
    

    RequestCreator的构造方法如下,核心是通过建造者模式建造Request实例。

    通过picasso可以发起请求、推迟(defer)请求、取消请求、检查缓存配置等操作;通过创建的Request实例data可以调用centerCrop()、centerInside()等方法。

    boolean shutdown;
    private final Picasso picasso;
    private final Request.Builder data;
    
    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);
    }
    

    3. 显示into(iv)

    核心逻辑为创建Action,即一次图片请求的活动过程,然后分发该action

    /* RequestCreator.class */
    /**
     * 异步请求图片资源至用户指定的ImageView
     * 使用弱引用关联ImageView,资源将会被自动回收
     */
    public void into(ImageView target) {
        into(target, null);
    }
    
    /**
     * 若用户指定Callback,为了防止Activity/Fragment回收,会是一个强引用;推荐及时调用cancelRequest()方法防止内存泄露
     */
    public void into(ImageView target, Callback callback) {
        // 记录开始时间,并检查线程是否安全
        long started = System.nanoTime();
        checkMain();
    
        if (target == null) {
            throw new IllegalArgumentException("Target must not be null.");
        }
    
        // 若Request实例为空,取消当前请求,设置占位图
        if (!data.hasImage()) {
            picasso.cancelRequest(target);
            if (setPlaceholder) {
                setPlaceholder(target, getPlaceholderDrawable());
            }
            return;
        }
    
        // deferred默认是false的
        if (deferred) {
            if (data.hasSize()) {
                throw new IllegalStateException("Fit cannot be used with resize.");
            }
            int width = target.getWidth();
            int height = target.getHeight();
            if (width == 0 || height == 0) {
                if (setPlaceholder) {
                    setPlaceholder(target, getPlaceholderDrawable());
                }
                // 若获取不到目标ImageView的宽高,就延迟请求,直到获取宽高时再次调用into()方法
                picasso.defer(target, new DeferredRequestCreator(this, target, callback));
                return;
            }
            data.resize(width, height);
        }
    
        // 创建Request实例并标记
        Request request = createRequest(started);
        String requestKey = createKey(request);
    
        // 控制从缓存中读取,若命中requestKey对应的Bitmap则取消当前请求,并显示图片
        if (shouldReadFromMemoryCache(memoryPolicy)) {
            Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
            if (bitmap != null) {
                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;
            }
        }
    
        if (setPlaceholder) {
            setPlaceholder(target, getPlaceholderDrawable());
        }
    
        // 内存缓存未命中,则创建Action实例,并提交至picasso
        Action action =
                new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
                        errorDrawable, requestKey, tag, callback, noFade);
    
        picasso.enqueueAndSubmit(action);
    }
    

    3.1 action的创建

    上述代码中用到的关键活动类ImageViewAction就是继承自Action,并重写了Action中的方法complete()、error()、cancel(),具体实现和方法注释说明如下。

    class ImageViewAction extends Action<ImageView> {
    
        Callback callback;
    
        ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
                        int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
                        Callback callback, boolean noFade) {
            super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,
                    tag, noFade);
            this.callback = callback;
        }
    
        // 图片请求成功,使用PicassoDrawable将图片显示到指定的ImageView控件中
        @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
            if (result == null) {
                throw new AssertionError(
                        String.format("Attempted to complete action with no result!\n%s", this));
            }
    
            ImageView target = this.target.get();
            if (target == null) {
                return;
            }
    
            Context context = picasso.context;
            boolean indicatorsEnabled = picasso.indicatorsEnabled;
            PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    
            if (callback != null) {
                callback.onSuccess();
            }
        }
    
        // 图片请求失败,判断是否显示占位图
        @Override public void error(Exception e) {
            ImageView target = this.target.get();
            if (target == null) {
                return;
            }
            Drawable placeholder = target.getDrawable();
            if (placeholder instanceof Animatable) {
                ((Animatable) placeholder).stop();
            }
            if (errorResId != 0) {
                target.setImageResource(errorResId);
            } else if (errorDrawable != null) {
                target.setImageDrawable(errorDrawable);
            }
    
            if (callback != null) {
                callback.onError(e);
            }
        }
    
        // 取消本次请求
        @Override void cancel() {
            super.cancel();
            if (callback != null) {
                callback = null;
            }
        }
    }
    

    3.2 action的提交

    picasso提交action,然后通过分发器dispatcher分发消息。

    /* Picasso.class */
    void enqueueAndSubmit(Action action) {
        ...
        submit(action);
    }
    
    void submit(Action action) {
        dispatcher.dispatchSubmit(action);
    }
    

    3.3 action的分发

    dispatcher通过Handler发送action,交由图片捕获器BitmapHunter处理。

    /* Dispatcher.class */
    void dispatchSubmit(Action action) {
        handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
    }
    
    @Override public void handleMessage(final Message msg) {
        switch (msg.what) {
            case REQUEST_SUBMIT: {
                Action action = (Action) msg.obj;
                dispatcher.performSubmit(action);
                break;
            ...
            }
        }
    }
    
    void performSubmit(Action action) {
        performSubmit(action, true);
    }
    
    void performSubmit(Action action, boolean dismissFailed) {
        ...
        // 剩下的工作将由BitmapHunter完成
        BitmapHunter hunter = BitmapHunter.forRequest(action.getPicasso(), this, cache, stats, action);
        hunter.future = service.submit(hunter);
        ...
    }
    

    3.3.1 BitmapHunter.forRequest()获取图片捕获器

    /* BitmapHunter.class */
    static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) {
        Request request = action.getRequest();
        List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
    
        for (int i = 0, count = requestHandlers.size(); i < count; i++) {
            RequestHandler requestHandler = requestHandlers.get(i);
            // 轮询获取能够处理这次活动请求(action's request)的请求处理器
            if (requestHandler.canHandleRequest(request)) {
                return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
            }
        }
    
        return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
    }
    

    其中所有的请求处理器requestHandlers是在Picasso构造时初始化的,总共为7种。

    Picasso(...) {
        ...
        allRequestHandlers.add(new ResourceRequestHandler(context));
        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));
        ...
    }
    

    看看网络请求处理器中的匹配规则,即Uri中包含"http"或"https"。

    /* NetworkRequestHandler.class */
    @Override public boolean canHandleRequest(Request data) {
        String scheme = data.uri.getScheme();
        return ("http".equals(scheme) || "https".equals(scheme));
    }
    

    3.3.2 service.submit(hunter)开启图片捕获器hunter线程,获取bitmap

    图片捕获器hunter是一个Runnable,hunter获取后执行submit()方法开启hunter线程。

    /* ExecutorService.class */
    public Future<?> submit(Runnable task) {
        ...
        execute(ftask);
        return ftask;
    }
    

    hunter线程中的核心操作就是获取图片资源,然后分发获取结果

    /* BitmapHunter.class */
    @Override public void run() {
    
        Bitmap 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);
            ...
            return bitmap;
        }
    
        // 缓存未命中,使用请求处理器加载图片资源
        RequestHandler.Result result = requestHandler.load(data, networkPolicy);
        if (result != null) {
            ...
            bitmap = result.getBitmap();
            ...
        }
        ...
        return bitmap;
    }
    

    本篇以请求网络图片为背景展开介绍,load()方法中使用下载器downloader来下载图片,当前Picasso版本中的网络下载器貌似只有OkHttp3Downloader,没有UrlConnectionDownloader了。

    网络请求处理器NetworkRequestHandler中的load()方法如下,即使用下载器下载图片资源。

    /* NetworkRequestHandler.class */
    @Override public Result load(Request request, int networkPolicy) throws IOException {
        okhttp3.Request downloaderRequest = createRequest(request, networkPolicy);
        Response response = downloader.load(downloaderRequest);
        ResponseBody body = response.body();
        ...
        return new Result(body.source(), loadedFrom);
    }
    

    网络下载器OkHttp3Downloader中的load()方法如下,请求完成即可获取图片资源。

    /* OkHttp3Downloader.class */
    @NonNull @Override public Response load(@NonNull Request request) throws IOException {
        return client.newCall(request).execute();
    }
    

    至此图片资源获取bitmap完成,hunter执行dispatcher.dispatchComplete(this);方法来将图片获取结果分发出去,最终图片会显示至ImageView控件,下面看看这个分发过程。

    3.3.3 bitmap获取后,消息分发

    /* Dispatcher.class */
    void dispatchComplete(BitmapHunter hunter) {
        handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
    }
    
    /* Dispatcher.class */
    case HUNTER_COMPLETE: {
        BitmapHunter hunter = (BitmapHunter) msg.obj;
        dispatcher.performComplete(hunter);
        break;
    }
    
    /* Dispatcher.class */
    void performComplete(BitmapHunter hunter) {
        ...
        batch(hunter);
        ...
    }
    
    /* Dispatcher.class */
    private void batch(BitmapHunter hunter) {
        ...
        batch.add(hunter);
        if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
            handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
        }
    }
    
    /* Dispatcher.class */
    case HUNTER_DELAY_NEXT_BATCH: {
        dispatcher.performBatchComplete();
        break;
    }
    
    /* Dispatcher.class */
    void performBatchComplete() {
        List<BitmapHunter> copy = new ArrayList<>(batch);
        batch.clear();
        mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
        logBatch(copy);
    }
    
    /* Picasso.class */
    case HUNTER_BATCH_COMPLETE: {
        @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
        for (int i = 0, n = batch.size(); i < n; i++) {
            BitmapHunter hunter = batch.get(i);
            hunter.picasso.complete(hunter);
        }
        break;
    }
    
    /* Picasso.class */
    void complete(BitmapHunter hunter) {
        Action single = hunter.getAction();
        ...
        Exception exception = hunter.getException();
        Bitmap result = hunter.getResult();
        LoadedFrom from = hunter.getLoadedFrom();
    
        if (single != null) {
            deliverAction(result, from, single, exception);
        }
        ...
    }
    
    /* Picasso.class */
    private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
        ...
        action.complete(result, from);
        ...
    }
    

    最后,本次action资源请求成功,调用其complete()方法,通过PicassoDrawable的setBitmap()方法来将图片显示至目标控件中。

    /* ImageViewAction.class */
    @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
        ...
        PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
        ...
    }
    

    PicassoDrawable的setBitmap()方法中,将获取到的bitmap转为drawable,然后设置进ImageView控件中,即可完成图片的显示。

    /* PicassoDrawable.class */
    static void setBitmap(ImageView target, Context context, Bitmap bitmap, Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
        ...
        PicassoDrawable drawable = new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
        target.setImageDrawable(drawable);
    }
    

    小结:上述流程即先在get()方法中创建Picasso,其中包含下载器downloader、缓存器cache、线程池service、请求转换器transformer、分发器dispatcher。然后在load(url)方法中创建请求Request实例,通过请求实例可以设置占位图,调整图片显示尺寸等操作。紧接着在into(iv)方法中创建一个请求图片的活动action,action被提交给了图片捕获器hunter线程,hunter启动后,先为action匹配合适的请求处理器requestHandler,然后requestHandler使用下载器downloader从网络下载图片资源,网络请求完成后,通过分发器dispatcher告知action本次图片资源获取成功。最后在action的complete()方法中使用PicassoDrawable将图片设置进目标控件,完成图片的显示。


    4. 额外说明

    下面对Picasso对象的各个核心内部对象简要的看一下。

    4.1 下载器downloader

    当前Picasso版本的下载器只有OkHttp3Downloader。

    4.2. 缓存器cache

    使用的是LruCache,具体的内存和磁盘缓存空间为RAM的15%和磁盘的2%且范围为5~50mb。

    memory cache of 15% the available application RAM,Disk cache of 2% storage space up to 50MB but no less than 5MB。

    4.3. 线程池service

    默认池数为3,网络状态为wifi时为4,4G时为3,3G时为2,2G时为1。

    private static final int DEFAULT_THREAD_COUNT = 3;
    
    switch (info.getType()) {
        case ConnectivityManager.TYPE_WIFI:
        case ConnectivityManager.TYPE_WIMAX:
        case ConnectivityManager.TYPE_ETHERNET:
            setThreadCount(4);
            break;
        case ConnectivityManager.TYPE_MOBILE:
            switch (info.getSubtype()) {
                case TelephonyManager.NETWORK_TYPE_LTE:  // 4G
                    setThreadCount(3);
                    break;
                case TelephonyManager.NETWORK_TYPE_UMTS: // 3G
                    setThreadCount(2);
                    break;
                case TelephonyManager.NETWORK_TYPE_GPRS: // 2G
                    setThreadCount(1);
                    break;
            }
            break;
    }
    

    4.4. 请求转换器transformer

    默认为不做任何转换处理,直接返回原请求。

    RequestTransformer IDENTITY = new RequestTransformer() {
        @Override public Request transformRequest(Request request) {
            return request;
        }
    };
    

    4.5. 分发器dispatcher

    主要就是通过Handler来分发各种消息。

    相关文章

      网友评论

          本文标题:简谈源码-Picasso源码(v_2.71828)

          本文链接:https://www.haomeiwen.com/subject/gpqphqtx.html