Glide学习笔记之回调监听

回调:将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);
        }
    }


猜你喜欢

转载自blog.csdn.net/qq_36447701/article/details/80525975
今日推荐