xUtils3源码解析之-----image

本文详细解析了xUtils3中图片加载模块的工作原理,包括ImageOptions配置、图片缓存策略、异步加载流程及UI回调机制等关键内容。

利用工作之外的时间终于看完了xutils3的所有模块的源码,以此博客来记录自己的分析过程,在此约定半年之后重新分析xUtils3源码对比该系列博客看看自己有没有新的突破。

1. xUtils3源码解析之—–image
2. xUtils3源码解析之—–Http(s)
3. xUtils3源码分析之—–DB(ORM)
4. xUtils3源码分析之—–view注解

前言

首先会列出最基本的使用方法,更详细的使用方法看xUtils3 demo。其次会结合基本使用方法分析源码的结构,以及代码流程。

基本使用方法

首先创建个ImageOptions配置一些照片的信息,x.image().bind(ImageView, url)显示图片(imageOptions,CustomBitmapLoadCallBack这2个参数选填);

imageOptions = new ImageOptions.Builder()
            .setSize(DensityUtil.dip2px(120), DensityUtil.dip2px(120))
            .setRadius(DensityUtil.dip2px(5))
            // 如果ImageView的大小不是定义为wrap_content, 不要crop.
            .setCrop(true) // 很多时候设置了合适的scaleType也不需要它.
            // 加载中或错误图片的ScaleType
            //.setPlaceholderScaleType(ImageView.ScaleType.MATRIX)
            .setImageScaleType(ImageView.ScaleType.CENTER_CROP)
            .setLoadingDrawableId(R.mipmap.ic_launcher)
            .setFailureDrawableId(R.mipmap.ic_launcher)
            .build();

ImageView iv = findViewById(R.id.iv);
String url = image_url;
x.image().bind(iv, url, imageOptions,
                        new CustomBitmapLoadCallBack(holder));

源码分析

x.image()得到ImageManagerImpl实例,接着通过ImageManagerImpl.bind(ImageView, url)显示图片;imageManagerImpl还有几个bind重载方法,其中imageview和url必填参数。我们这里选参数最多的一个重载方法,该方法通过x.task()得到TaskControllerImpl实例(xUtils3初始化的时候创建的),接着调用taskControllerImpl.autoPost(runnable)方法,如果当前线程是Ui线程autoPost(runnable)方法会直接执行传入线程的run方法,如果不是Ui线程则通过(关于Handler消息机制可以看看)handler.post(runnable)方法切换到Ui线程执行run方法。我们回到bind()方法里面线程的run()方法

org.xutils.image.ImageManagerImpl;类

@Override
public void bind(final ImageView view, final String url, final ImageOptions options, final Callback.CommonCallback<Drawable> callback) {
    x.task().autoPost(new Runnable() {
        @Override
        public void run() {
            ImageLoader.doBind(view, url, options, callback);
        }
    });
}

org.xutils.common.task.TaskControllerImpl;类

@Override
public void autoPost(Runnable runnable) {
    if (runnable == null) return;
    if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
        runnable.run();
    } else {
        TaskProxy.sHandler.post(runnable);
    }
}

ImageLoader.doBind(view, url, options, callback)首先检查参数,然后设置最大的size,如果内存中有则直接从内存(LRU算法以MemCacheKey为key,Drawable为value)中获取,否则通过network or DiskCache,我们接着看ImageLoader().doLoad(view, url, localOptions, callback);

static Cancelable doBind(final ImageView view,
                             final String url,
                             final ImageOptions options,
                             final Callback.CommonCallback<Drawable> callback) {
    // check params
    ImageOptions localOptions = options;
    {
        if (view == null) {
            postArgsException(null, localOptions, "view is null", callback);
            return null;
        }

        if (TextUtils.isEmpty(url)) {
            postArgsException(view, localOptions, "url is null", callback);
            return null;
        }

        if (localOptions == null) {
            localOptions = ImageOptions.DEFAULT;
        }
        localOptions.optimizeMaxSize(view);
    }

    // stop the old loader
    MemCacheKey key = new MemCacheKey(url, localOptions);
    Drawable oldDrawable = view.getDrawable();
    if (oldDrawable instanceof AsyncDrawable) {
        ImageLoader loader = ((AsyncDrawable) oldDrawable).getImageLoader();
        if (loader != null && !loader.stopped) {
            if (key.equals(loader.key)) {
                // repetitive url and options binding to the same View.
                // not need callback to ui.
                return null;
            } else {
                loader.cancel();
            }
        }
    } else if (oldDrawable instanceof ReusableDrawable) {
        MemCacheKey oldKey = ((ReusableDrawable) oldDrawable).getMemCacheKey();
        if (oldKey != null && oldKey.equals(key)) {
            MEM_CACHE.put(key, oldDrawable);
        }
    }

    // load from Memory Cache
    Drawable memDrawable = null;
    if (localOptions.isUseMemCache()) {
        memDrawable = MEM_CACHE.get(key);
        if (memDrawable instanceof BitmapDrawable) {
            Bitmap bitmap = ((BitmapDrawable) memDrawable).getBitmap();
            if (bitmap == null || bitmap.isRecycled()) {
                memDrawable = null;
            }
        }
    }
    if (memDrawable != null) { // has mem cache
        boolean trustMemCache = false;
        try {
            if (callback instanceof ProgressCallback) {
                ((ProgressCallback) callback).onWaiting();
            }
            // hit mem cache
            view.setScaleType(localOptions.getImageScaleType());
            view.setImageDrawable(memDrawable);
            trustMemCache = true;
            if (callback instanceof CacheCallback) {
                trustMemCache = ((CacheCallback<Drawable>) callback).onCache(memDrawable);
                if (!trustMemCache) {
                    // not trust the cache
                    // load from Network or DiskCache
                    return new ImageLoader().doLoad(view, url, localOptions, callback);
                }
            } else if (callback != null) {
                callback.onSuccess(memDrawable);
            }
        } catch (Throwable ex) {
            LogUtil.e(ex.getMessage(), ex);
            // try load from Network or DiskCache
            trustMemCache = false;
            return new ImageLoader().doLoad(view, url, localOptions, callback);
        } finally {
            if (trustMemCache && callback != null) {
                try {
                    callback.onFinished();
                } catch (Throwable ignored) {
                    LogUtil.e(ignored.getMessage(), ignored);
                }
            }
        }
    } else {
        // load from Network or DiskCache
        return new ImageLoader().doLoad(view, url, localOptions, callback);
    }
    return null;
}

接口回调分析

由上 new ImageLoader实例,并通过doLoad方法处理网络和本地缓存来显示图片,该方法首先是一些属性的初始化其中ImageView是通过弱引用保存的;然后给imageview设置预加载的图片,该预加载图片通过AsyncDrawable(imageLoader, view)实例包装其中imageLoader属性也是通过弱引用保存的后期会用到该属性,接着通过用户传入的参数创建RequestParams, 再通过x.http().get(params, callback)网络请求(这里有一篇网络请求的过程的文章),我们直接看接口回调是怎么处理的吧。

private Cancelable doLoad(ImageView view,
                          String url,
                          ImageOptions options,
                          Callback.CommonCallback<Drawable> callback) {

    this.viewRef = new WeakReference<ImageView>(view);
    this.options = options;
    this.key = new MemCacheKey(url, options);
    this.callback = callback;
    if (callback instanceof Callback.ProgressCallback) {
        this.progressCallback = (Callback.ProgressCallback<Drawable>) callback;
    }
    if (callback instanceof Callback.PrepareCallback) {
        this.prepareCallback = (Callback.PrepareCallback<File, Drawable>) callback;
    }
    if (callback instanceof Callback.CacheCallback) {
        this.cacheCallback = (Callback.CacheCallback<Drawable>) callback;
    }

    // set loadingDrawable
    Drawable loadingDrawable = null;
    if (options.isForceLoadingDrawable()) {
        loadingDrawable = options.getLoadingDrawable(view);
        view.setScaleType(options.getPlaceholderScaleType());
        view.setImageDrawable(new AsyncDrawable(this, loadingDrawable));
    } else {
        loadingDrawable = view.getDrawable();
        view.setImageDrawable(new AsyncDrawable(this, loadingDrawable));
    }

    // request
    RequestParams params = createRequestParams(url, options);
    if (view instanceof FakeImageView) {
        synchronized (FAKE_IMG_MAP) {
            FAKE_IMG_MAP.put(url, (FakeImageView) view);
        }
    }
    return cancelable = x.http().get(params, this);
}

接口onStarted方法回调分析

该类实现了Callback.ProgressCallback接口,我们一次看接口下的方法,这些方法无外乎处理结果然后将结果回调给用户。先看onStarted()回调方法,注意到validView4Callback(boolean)方法,该方法判断view是否有效,总要有几个判定条件:同时满足1 2 3 或者 1 2 4该view才有效。
1.弱引用的view没有被回收;
2.AsyncDrawable包装预加载图片的实例中的弱引用imageLoader没有被回收;
3.当前imageLoad等于 2获取到的imageLoad并且view不可见;
4.当前imageLoad不等于 2获取到的imageLoad,当前imageLoad.seq > 2获取到的imageLoad.req;(req:当前实例是类的第几个实例,通过静态原子变量AtomicLong从0开始)

 public interface CommonCallback<ResultType> extends Callback {
        void onSuccess(ResultType result);
        void onError(Throwable ex, boolean isOnCallback);
        void onCancelled(CancelledException cex);
        void onFinished();
    }

public interface ProgressCallback<ResultType> extends CommonCallback<ResultType> {
        void onWaiting();
        void onStarted();
        void onLoading(long total, long current, boolean isDownloading);
}

**该接口方法的具体实现如下:**

@Override
public void onWaiting() {
    if (progressCallback != null) {
        progressCallback.onWaiting();
    }
}

@Override
public void onStarted() {
    if (validView4Callback(true) && progressCallback != null) {
        progressCallback.onStarted();
    }
}

@Override
public void onLoading(long total, long current, boolean isDownloading) {
    if (validView4Callback(true) && progressCallback != null) {
        progressCallback.onLoading(total, current, isDownloading);
    }
}

@Override
public void onSuccess(Drawable result) {
    if (!validView4Callback(!hasCache)) return;

    if (result != null) {
        setSuccessDrawable4Callback(result);
        if (callback != null) {
            callback.onSuccess(result);
        }
    }
}

@Override
public void onError(Throwable ex, boolean isOnCallback) {
    stopped = true;
    if (!validView4Callback(false)) return;

    if (ex instanceof FileLockedException) {
        LogUtil.d("ImageFileLocked: " + key.url);
        x.task().postDelayed(new Runnable() {
            @Override
            public void run() {
                doBind(viewRef.get(), key.url, options, callback);
            }
        }, 10);
        return;
    }

    LogUtil.e(key.url, ex);
    setErrorDrawable4Callback();
    if (callback != null) {
        callback.onError(ex, isOnCallback);
    }
}

 @Override
public void onCancelled(CancelledException cex) {
    stopped = true;
    if (!validView4Callback(false)) return;

    if (callback != null) {
        callback.onCancelled(cex);
    }
}

@Override
public void onFinished() {
    stopped = true;
    ImageView view = viewRef.get();
    if (view instanceof FakeImageView) {
        synchronized (FAKE_IMG_MAP) {
            FAKE_IMG_MAP.remove(key.url);
        }
    }

    if (!validView4Callback(false)) return;

    if (callback != null) {
        callback.onFinished();
    }
}

接口onSuccess方法回调分析

我们接着看onSuccess回调方法,首先也是通过判断view是否有效,然后通过setSuccessDrawable4Callback(result)方法设置imageView(其中也有用户通过ImageOptions配置的图片信息), 最后回调用户的接口。

@Override
public void onSuccess(Drawable result) {
    if (!validView4Callback(!hasCache)) return;

    if (result != null) {
        setSuccessDrawable4Callback(result);
        if (callback != null) {
            callback.onSuccess(result);
        }
    }
}

private void setSuccessDrawable4Callback(final Drawable drawable) {
    final ImageView view = viewRef.get();
    if (view != null) {
        view.setScaleType(options.getImageScaleType());
        if (drawable instanceof GifDrawable) {
            if (view.getScaleType() == ImageView.ScaleType.CENTER) {
                view.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
            }
            view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
        if (options.getAnimation() != null) {
            ImageAnimationHelper.animationDisplay(view, drawable, options.getAnimation());
        } else if (options.isFadeIn()) {
            ImageAnimationHelper.fadeInDisplay(view, drawable);
        } else {
            view.setImageDrawable(drawable);
        }
    }
}

接口onError方法回调分析

接着看请求失败的回调方法onError, 首先设置stopped状态,接着判断view是否可用;如果异常 instanceOf FileLockedException(该异常可能在取本地缓存文件的时候该文件被其他线程占用时产生的。)则重新调用doBind()回到了最开始的步骤。setErrorDrawable4Callback()方法设置imageview显示的失败的资源(该资源是ImageOptions用户配置的信息),最后回调用户的接口。

 @Override
public void onError(Throwable ex, boolean isOnCallback) {
    stopped = true;
    if (!validView4Callback(false)) return;

    if (ex instanceof FileLockedException) {
        LogUtil.d("ImageFileLocked: " + key.url);
        x.task().postDelayed(new Runnable() {
            @Override
            public void run() {
                doBind(viewRef.get(), key.url, options, callback);
            }
        }, 10);
        return;
    }

    LogUtil.e(key.url, ex);
    setErrorDrawable4Callback();
    if (callback != null) {
        callback.onError(ex, isOnCallback);
    }
}

接口onFinished方法回调分析

看最后一个回调方法onFinished, 首先设置stopped状态,接着如果view instanceof FakeImageView 则从FAKE_IMG_MAP集合中去除,然后判断view是否有效,最后回调用户的接口。

@Override
public void onFinished() {
    stopped = true;
    ImageView view = viewRef.get();
    if (view instanceof FakeImageView) {
        synchronized (FAKE_IMG_MAP) {
            FAKE_IMG_MAP.remove(key.url);
        }
    }

    if (!validView4Callback(false)) return;

    if (callback != null) {
        callback.onFinished();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值