回调:将imageView的实例传到into()中,当Glide将图片加载完后以后,图片就能显示到ImageView上去了
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
// 构建target
return into(glide.buildImageViewTarget(view, transcodeClass));
}
public class ImageViewTargetFactory { // 最终构建出来的target(一般情况下回事GlideDrawableIamgeViewTarget,当使用asBitmap的时候回构建出BitmapImageViewTarget对象) @SuppressWarnings("unchecked") public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) { if (GlideDrawable.class.isAssignableFrom(clazz)) { return (Target<Z>) new GlideDrawableImageViewTarget(view); } else if (Bitmap.class.equals(clazz)) { return (Target<Z>) new BitmapImageViewTarget(view); } else if (Drawable.class.isAssignableFrom(clazz)) { return (Target<Z>) new DrawableImageViewTarget(view); } else { throw new IllegalArgumentException("Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)"); } } }
回到into()方法中最终会通过buildRequest(target)把这个target传到GenericRqeust中,所以在GenericRequest中的target.onResourceReady,就是调用的GlideDrawableIamgeViewTarget的onResourceReady。
@Override public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) { if (!resource.isAnimated()) { //TODO: Try to generalize this to other sizes/shapes. // This is a dirty hack that tries to make loading square thumbnails and then square full images less costly // by forcing both the smaller thumb and the larger version to have exactly the same intrinsic dimensions. // If a drawable is replaced in an ImageView by another drawable with different intrinsic dimensions, // the ImageView requests a layout. Scrolling rapidly while replacing thumbs with larger images triggers // lots of these calls and causes significant amounts of jank. float viewRatio = view.getWidth() / (float) view.getHeight(); float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight(); if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN && Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) { resource = new SquaringDrawable(resource, view.getWidth()); } } super.onResourceReady(resource, animation); this.resource = resource; resource.setLoopCount(maxLoopCount); resource.start(); }
@Override protected void setResource(GlideDrawable resource) { view.setImageDrawable(resource); }在这个方法中处理了图片的展示和GIF播放的逻辑,一张图片就显示出来了,这也就是Glide的一个回调
通过观察可以发现into()还可以直接传入一个target对象,所以我们可以自定义target来实现Glide的回调了观察target的继承图可以发现我们自诩在SimpleTarget和ViewTarget的基础上自定义就好了
SimpleTarget用法:
这里的泛型可以直接是Bitmap
SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() { @Override pulic void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) { // 在这里我们就可以拿到图片对象的实例resource imageView.setImageDrawable(resource); } };
pulic void loadImage(View view) { Glide.with(this) .load(url) .into(simpleTarget); }ViewTarget用法:
创建一个自定义的布局:
public class MyLayout extends LinearLayout { private final ViewTarget<MyLayout, GlideDrawable> viewTarget; public MyLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); viewTarget = new ViewTarget<MyLayout, GlideDrawable>(this) { @Override public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) { MyLayout myLayout = getView(); myLayout.setImageBackgroung(resource); } }; } public ViewTarget<MyLayout, GlideDrawable> getTarget() { return viewTarget; } public void setImageBackgroung(GlideDrawable resource) { setBackground(resource); } }
在activity中调用
public class MainActivity extends AppCompatActivity { public static final String url = "http://upload.sichuan.95504.net/mobile/special/s0/s0_05677934088858712.jpg?token=asdbjksdfjkasasjfb"; private MyLayout mLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLayout = findViewById(R.id.iv); } public void loadImg(View view) { Glide.with(getApplicationContext()) .load(new MyGlideUrl(url)) .into(mLayout.getTarget()); } }preload()预加载
用法:
在加载图片前先使用预加载将图片加入到缓存中,可以提高加载的速度
Glide.with(this).load(url).preload(); Glide.with(this).load(url).into(imageView);
public Target<TranscodeType> preload(int width, int height) { final PreloadTarget<TranscodeType> target = PreloadTarget.obtain(width, height); return into(target); } public Target<TranscodeType> preload() { return preload(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL); } public final class PreloadTarget<Z> extends SimpleTarget<Z> { /** * Returns a PreloadTarget. * * @param width The width in pixels of the desired resource. * @param height The height in pixels of the desired resource. * @param <Z> The type of the desired resource. */ public static <Z> PreloadTarget<Z> obtain(int width, int height) { return new PreloadTarget<Z>(width, height); } private PreloadTarget(int width, int height) { super(width, height); } @Override public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) { // 这里是告诉Glide图片已经缓存好了,可以释放资源了,而不是清除 Glide.clear(this); } }downloadonly()缓存图片到文件中
用法:
downloadOnly(int widht,int height);设置宽高,用于在子线程中下载
downloadOnly(Y target)传入泛型,用于在主线程中下载
在子线程中下载图片:
private void downloadImage(View view) { new Thread(new Runnable() { @Override public void run() { try { FutureTarget<File> target = Glide.with(getApplicationContext()) .load(url) .downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL); final File imageFile = target.get(); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), imageFile.getPath(), Toast.LENGTH_SHORT).show(); Glide.with(getApplicationContext()) .load(imageFile) .into(mLayout.getTarget()); } }); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }).start(); }downloadOnly源码:
public FutureTarget<File> downloadOnly(int width, int height) { return getDownloadOnlyRequest().downloadOnly(width, height); }获取一个GenericTranscodeRequest对象
private GenericTranscodeRequest<ModelType, InputStream, File> getDownloadOnlyRequest() { return optionsApplier.apply(new GenericTranscodeRequest<ModelType, InputStream, File>(File.class, this, streamModelLoader, InputStream.class, File.class, optionsApplier)); }
public FutureTarget<File> downloadOnly(int width, int height) { return getDownloadOnlyRequest().into(width, height); }构建一个图片下载的请求
private GenericRequestBuilder<ModelType, DataType, File, File> getDownloadOnlyRequest() { ResourceTranscoder<File, File> transcoder = UnitTranscoder.get(); DataLoadProvider<DataType, File> dataLoadProvider = glide.buildDataProvider(dataClass, File.class); FixedLoadProvider<ModelType, DataType, File, File> fixedLoadProvider = new FixedLoadProvider<ModelType, DataType, File, File>(modelLoader, transcoder, dataLoadProvider); return optionsApplier.apply(new GenericRequestBuilder<ModelType, DataType, File, File>(fixedLoadProvider, File.class, this)) .priority(Priority.LOW) .diskCacheStrategy(DiskCacheStrategy.SOURCE) .skipMemoryCache(true); }into()首先
public FutureTarget<TranscodeType> into(int width, int height) { final RequestFutureTarget<ModelType, TranscodeType> target = new RequestFutureTarget<ModelType, TranscodeType>(glide.getMainHandler(), width, height); // TODO: Currently all loads must be started on the main thread... glide.getMainHandler().post(new Runnable() { @Override public void run() { if (!target.isCancelled()) { // 将RequestFutureTarget对象传入到GenericRqeustBUilder中的into into(target); } } }); return target; }通过前面对downloadOnly的基础用法可以知道:
调用downloadOnly()之后再调用get()就可以得到图片的下载文件
这里的get()又是谁的????
因为downloadOnly()返回的是一个FutureTarget的对象,也就是RequestFutureTarget的对象
@Override public R get() throws InterruptedException, ExecutionException { try { // 实际上调用的是这里 return doGet(null); } catch (TimeoutException e) { throw new AssertionError(e); } }
private synchronized R doGet(Long timeoutMillis) throws ExecutionException, InterruptedException, TimeoutException { if (assertBackgroundThread) { Util.assertBackgroundThread(); } if (isCancelled) { throw new CancellationException(); } else if (exceptionReceived) { throw new ExecutionException(exception); } else if (resultReceived) { // 判断是否下载完成,完成就直接返回 return resource; } if (timeoutMillis == null) { // 这边有个waiter线程等待 waiter.waitForTimeout(this, 0); } else if (timeoutMillis > 0) { waiter.waitForTimeout(this, timeoutMillis); } if (Thread.interrupted()) { throw new InterruptedException(); } else if (exceptionReceived) { throw new ExecutionException(exception); } else if (isCancelled) { throw new CancellationException(); } else if (!resultReceived) { throw new TimeoutException(); } return resource; }
static class Waiter { public void waitForTimeout(Object toWaitOn, long timeoutMillis) throws InterruptedException { toWaitOn.wait(timeoutMillis); } public void notifyAll(Object toNotify) { toNotify.notifyAll(); } }这也说明了为什么downloadOnly(int widht,int height)会在子线程中,如果放在主线程就会造成线程卡死
现在线程卡死又什么时候恢复呢?
@Override public synchronized void onResourceReady(R resource, GlideAnimation<? super R> glideAnimation) { // We might get a null result. // 图片文件已经下载好,下次咋低调用get()时就不会阻塞线程了 resultReceived = true; // 图片文件赋值给全局变量,在doGet()就能访问到resource了 this.resource = resource; // 通知所有的waiter取消线程阻塞 waiter.notifyAll(this); }这就是downloadOnly(int widht,int height)的基本用法和实现原理
downloadOnly(Y target)则是要求我们传入一个target,就不受RequestFutureTarget限制了
用法:自定义自己的Target实现Target接口(泛型必须是File类型的)
public class DownloadImageTarget implements Target<File> { @Override public void onLoadStarted(Drawable placeholder) { } @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { } @Override public void onResourceReady(File resource, GlideAnimation<? super File> glideAnimation) { //下载完成以后会回调到此处 Log.d("downloadPath::::", resource.getPath()); } @Override public void onLoadCleared(Drawable placeholder) { } @Override public void getSize(SizeReadyCallback cb) { //Glide在开始加载图片的时候会计算图片的大小,回调到onSizeReady()中( Target.SIZE_ORIGINAL表示原始图片) cb.onSizeReady(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL); } @Override public void setRequest(Request request) { } @Override public Request getRequest() { return null; } @Override public void onStart() { } @Override public void onStop() { } @Override public void onDestroy() { } }在activity中调用:
private void downloadImage(View view) { Glide.with(this) .load(url) .downloadOnly(new DownloadImageTarget()); }
listener()监听
基本用法:
Glide.with(this) .load(url) .listener(new RequestListener<File, GlideDrawable>() { @Override // 加载失败 public boolean onException(Exception e, File model, Target<GlideDrawable> target, boolean isFirstResource) { // 返回true表示事件处理掉,就不向下执行 return false; } @Override // 加载成功 public boolean onResourceReady(GlideDrawable resource, File model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { // 返回true表示事件处理掉,就不向下执行 return false; } }) .into(imageView);listener()源码:
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> listener( RequestListener<? super ModelType, TranscodeType> requestListener) { // 在构建GenericRequest的时候会把listener传进去 this.requestListener = requestListener; return this; }所以在图片加载完成的时候:
private void onResourceReady(Resource<?> resource, R result) { // We must call isFirstReadyResource before setting status. boolean isFirstResource = isFirstReadyResource(); status = Status.COMPLETE; this.resource = resource; // 在这会通过requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,isFirstResource)这个就是我们在使用的时候所传入的boolean值 // 判断是否调用target的onResourceReady() if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache, isFirstResource)) { GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource); target.onResourceReady(result, animation); } notifyLoadSuccess(); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: " + (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache); } }同理onException()也是一样的
@Override public void onException(Exception e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "load failed", e); } status = Status.FAILED; //TODO: what if this is a thumbnail request? // 在此处判断传入的Boolean值 if (requestListener == null || !requestListener.onException(e, model, target, isFirstReadyResource())) { setErrorPlaceholder(e); } }