Glide源码阅读之模板模式2
续接《Glide设计模式之模板模式1》
LifecycleListener已知的实现类列表如下:
AppWidgetTargetBaseTargetBitmapImageViewTargetBitmapThumbnailImageViewTargetCustomTargetCustomViewTargetDrawableImageViewTarget- DrawableThumbnailImageViewTarget
ImageViewTarget- NotificationTarget
- PreloadTarget
- RequestFutureTarget
- RequestManager
- SimpleTarget
- TargetTracker
- ThumbnailImageViewTarget
ViewTarget
DrawableThumbnailImageViewTarget
com.bumptech.glide.request.target.DrawableThumbnailImageViewTarget
高效地显示多个连续加载到一个视图中的Drawables。
/**
* Efficiently displays multiple Drawables loaded serially into a single {@link android.view.View}.
*/
高效地显示多个连续加载到一个视图中的Drawables。
// Public API.
@SuppressWarnings("unused")
public class DrawableThumbnailImageViewTarget extends ThumbnailImageViewTarget<Drawable> {
public DrawableThumbnailImageViewTarget(ImageView view) {
super(view);
}
/** @deprecated Use {@link #waitForLayout()} instead. */
@Deprecated
@SuppressWarnings("deprecation")
public DrawableThumbnailImageViewTarget(ImageView view, boolean waitForLayout) {
super(view, waitForLayout);
}
@Override
protected Drawable getDrawable(Drawable resource) {
return resource;
}
}
NotificationTarget
com.bumptech.glide.request.target.NotificationTarget
这个类用于通过RemoteViews在一个通知的ImageView中显示下载的位图。
注意:为了使取消正常工作,您必须在每次后续加载时传入这个类的相同实例。
/**
* This class is used to display downloaded Bitmap inside an ImageView of a Notification through
* RemoteViews.
*
* <p>Note - For cancellation to work correctly, you must pass in the same instance of this class
* for every subsequent load.
*/
这个类用于通过RemoteViews在一个通知的ImageView中显示下载的位图。
注意:为了使取消正常工作,您必须在每次后续加载时传入这个类的相同实例。
// Public API.
@SuppressWarnings({"WeakerAccess", "unused"})
public class NotificationTarget extends CustomTarget<Bitmap> {
PreloadTarget
com.bumptech.glide.request.target.PreloadTarget
一次性使用的Target类,它将资源加载到内存中,然后清除自己。
/**
* A one time use {@link com.bumptech.glide.request.target.Target} class that loads a resource into
* memory and then clears itself.
*
* @param <Z> The type of resource that will be loaded into memory.
*/
一次性使用的Target类,它将资源加载到内存中,然后清除自己
public final class PreloadTarget<Z> extends CustomTarget<Z> {
private static final int MESSAGE_CLEAR = 1;
private static final Handler HANDLER =
new Handler(
Looper.getMainLooper(),
new Callback() {
@Override
public boolean handleMessage(Message message) {
if (message.what == MESSAGE_CLEAR) {
((PreloadTarget<?>) message.obj).clear();
return true;
}
return false;
}
});
private final RequestManager requestManager;
/**
* 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(RequestManager requestManager, int width, int height) {
return new PreloadTarget<>(requestManager, width, height);
}
private PreloadTarget(RequestManager requestManager, int width, int height) {
super(width, height);
this.requestManager = requestManager;
}
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
// If a thumbnail request is set and the thumbnail completes, we don't want to cancel the
// primary load. Instead we wait until the primary request (the one set on the target) says
// that it is complete.
// Note - Any thumbnail request that does not complete before the primary request will be
// cancelled and may not be preloaded successfully. Cancellation of outstanding thumbnails after
// the primary request succeeds is a common behavior of all Glide requests and we're not trying
// to override it here.
Request request = getRequest();
if (request != null && request.isComplete()) {
HANDLER.obtainMessage(MESSAGE_CLEAR, this).sendToTarget();
}
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
// Do nothing, we don't retain a reference to our resource.
}
@SuppressWarnings("WeakerAccess")
@Synthetic
void clear() {
requestManager.clear(this);
}
}
RequestFutureTarget
com.bumptech.glide.request.RequestFutureTarget
一个未来的Glide实现,可用于在后台线程以阻塞方式加载资源
/**
* A {@link java.util.concurrent.Future} implementation for Glide that can be used to load resources
* in a blocking manner on background threads.
*
* <p>Note - Unlike most targets, RequestFutureTargets can be used once and only once. Attempting to
* reuse a RequestFutureTarget will probably result in undesirable behavior or exceptions. Instead
* of reusing objects of this class, the pattern should be:
*
* <pre>{@code
* FutureTarget<File> target = null;
* RequestManager requestManager = Glide.with(context);
* try {
* target = requestManager
* .downloadOnly()
* .load(model)
* .submit();
* File downloadedFile = target.get();
* // ... do something with the file (usually throws IOException)
* } catch (ExecutionException | InterruptedException | IOException e) {
* // ... bug reporting or recovery
* } finally {
* // make sure to cancel pending operations and free resources
* if (target != null) {
* target.cancel(true); // mayInterruptIfRunning
* }
* }
* }</pre>
*
* The {@link #cancel(boolean)} call will cancel pending operations and make sure that any resources
* used are recycled.
*
* @param <R> The type of the resource that will be loaded.
*/
一个未来的Glide实现,可用于在后台线程以阻塞方式加载资源。
注意:与大多数目标不同,RequestFutureTargets只能被使用一次。试图重用RequestFutureTarget可能会导致不良的行为或异常。与其重用这个类的对象,模式应该是:
FutureTarget<文件>目标= null;
RequestManager RequestManager = Glide.with(context);
尝试{
目标= requestManager
.downloadOnly ()
.load(模型)
。submit ();
filedownloaddfile = target.get();
/ /……对文件做一些事情(通常抛出IOException)
} catch (ExecutionException | InterruptedException | IOException e) {
/ /……Bug报告或修复
最后}{
//确保取消挂起的操作和释放资源
If (target != null) {
target.cancel(真正的);/ / mayInterruptIfRunning
}
}
cancel(boolean)调用将取消挂起的操作,并确保所有使用的资源都被回收。
public class RequestFutureTarget<R> implements FutureTarget<R>, RequestListener<R> {
FutureTarget
com.bumptech.glide.request.FutureTarget
既是目标又是未来的对象的接口
/**
* An interface for an object that is both a {@link com.bumptech.glide.request.target.Target} and a
* {@link java.util.concurrent.Future}. For example:
*
* <pre>{@code
* FutureTarget<Bitmap> futureTarget = Glide.with(fragment)
* .load("http://goo.gl/1asf12")
* .asBitmap()
* .into(250, 250);
* Bitmap myBitmap = futureTarget.get();
* ... // do things with bitmap and then release when finished:
* futureTarget.cancel(false);
* }</pre>
*
* <p>Note - {@link #get()} and {@link #get(long, java.util.concurrent.TimeUnit)} must be called off
* of the main thread or they will block forever.
*
* @param <R> The type of resource this FutureTarget will retrieve.
*/
既是目标又是未来的对象的接口。例如:
FutureTarget<Bitmap> FutureTarget = Glide.with(fragment)
.load(“http://goo.gl/1asf12”)
.asBitmap ()
.into(250、250);
Bitmap myBitmap = futureTarget.get();
... //使用bitmap做事情,然后在完成时释放:
futureTarget.cancel(假);
注意- Future.get()和Future。get(long, java.util.concurrent.TimeUnit)必须从主线程中调用,否则它们将永远阻塞。
public interface FutureTarget<R> extends Future<R>, Target<R> {}
RequestManager
com.bumptech.glide.RequestManager
用于管理和启动Glide请求的类。可以使用活动、片段和连接生命周期事件来智能地停止、启动和重新启动请求。通过实例化一个新对象来检索,或者利用内置的Activity和Fragment生命周期处理,使用静态Glide。用Fragment或Activity加载方法。
/**
* A class for managing and starting requests for Glide. Can use activity, fragment and connectivity
* lifecycle events to intelligently stop, start, and restart requests. Retrieve either by
* instantiating a new object, or to take advantage built in Activity and Fragment lifecycle
* handling, use the static Glide.load methods with your Fragment or Activity.
*
* @see Glide#with(android.app.Activity)
* @see Glide#with(androidx.fragment.app.FragmentActivity)
* @see Glide#with(android.app.Fragment)
* @see Glide#with(androidx.fragment.app.Fragment)
* @see Glide#with(Context)
*/
用于管理和启动Glide请求的类。可以使用活动、片段和连接生命周期事件来智能地停止、启动和重新启动请求。通过实例化一个新对象来检索,或者利用内置的Activity和Fragment生命周期处理,使用静态Glide。用Fragment或Activity加载方法。
public class RequestManager
implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
private static final RequestOptions DECODE_TYPE_BITMAP = decodeTypeOf(Bitmap.class).lock();
private static final RequestOptions DECODE_TYPE_GIF = decodeTypeOf(GifDrawable.class).lock();
private static final RequestOptions DOWNLOAD_ONLY_OPTIONS =
diskCacheStrategyOf(DiskCacheStrategy.DATA).priority(Priority.LOW).skipMemoryCache(true);
protected final Glide glide;
protected final Context context;
@SuppressWarnings("WeakerAccess")
@Synthetic
final Lifecycle lifecycle;
@GuardedBy("this")
private final RequestTracker requestTracker;
@GuardedBy("this")
private final RequestManagerTreeNode treeNode;
@GuardedBy("this")
private final TargetTracker targetTracker = new TargetTracker();
private final Runnable addSelfToLifecycle =
new Runnable() {
@Override
public void run() {
lifecycle.addListener(RequestManager.this);
}
};
/**
* Lifecycle callback that registers for connectivity events (if the
* android.permission.ACCESS_NETWORK_STATE permission is present) and restarts failed or paused
* requests.
*/
@Override
public synchronized void onStart() {
resumeRequests();
targetTracker.onStart();
}
/**
* Lifecycle callback that unregisters for connectivity events (if the
* android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
*/
@Override
public synchronized void onStop() {
pauseRequests();
targetTracker.onStop();
}
/**
* Lifecycle callback that cancels all in progress requests and clears and recycles resources for
* all completed requests.
*/
@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
Util.removeCallbacksOnUiThread(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
RequestManager是一个比较复杂的类。涉及的业务逻辑比较多,本文只关注从接口和抽象类集成过了后实现的部分,专注模板模式的业务实现。便于理解
SimpleTarget
com.bumptech.glide.request.target.SimpleTarget
一个简单的Target基类,具有非基本方法的默认(通常为no-op)实现,允许调用者指定确切的宽度/高度。
已弃用
/**
* A simple {@link com.bumptech.glide.request.target.Target} base class with default (usually no-op)
* implementations of non essential methods that allows the caller to specify an exact width/height.
* Typically use cases look something like this:
*
* <pre>
* <code>
* Target<Bitmap> target =
* Glide.with(fragment)
* .asBitmap()
* .load("http://somefakeurl.com/fakeImage.jpeg")
* .apply(fitCenterTransform())
* .into(new SimpleTarget<Bitmap>(250, 250) {
*
* {@literal @Override}
* public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
* // Do something with bitmap here.
* }
*
* });
* }
* // At some later point, clear the Target to release the resources, prevent load queues from
* // blowing out proportion, and to improve load times for future requests:
* Glide.with(fragment).clear(target);
* </code>
* </pre>
*
* <p><em>Warning!</em> this class is extremely prone to mis-use. Use SimpleTarget only as a last
* resort. {@link ViewTarget} or a subclass of {@link ViewTarget} is almost always a better choice.
*
* <p><em>Don't forget to clear instances of this class!</em>. If you must use this class, keep in
* mind that unlike {@link ViewTarget} it is not safe to load into new instances of this class
* repeatedly if every instance updates the same underlying {@link View} or caller. If you need to
* load into the same {@link View} or caller repeatedly using this class, always retain a reference
* to the previous instance and either call {@link com.bumptech.glide.RequestManager#clear(Target)}
* on the old instance before starting a new load or you must re-use the old instance for the new
* load. Glide's {@link com.bumptech.glide.RequestBuilder#into(Target)} method returns the {@link
* Target} instance you provided to make retaining a reference to the {@link Target} as easy as
* possible. That said, you must wait until you're completely finished with the resource before
* calling {@link com.bumptech.glide.RequestManager#clear(Target)} and you should always null out
* references to any loaded resources in {@link Target#onLoadCleared(Drawable)}.
*
* <p>Always try to provide a size when using this class. Use {@link SimpleTarget#SimpleTarget(int,
* int)} whenever possible with values that are <em>not</em> {@link Target#SIZE_ORIGINAL}. Using
* {@link Target#SIZE_ORIGINAL} is unsafe if you're loading large images or are running your
* application on older or memory constrained devices because it can cause Glide to load very large
* images into memory. In some cases those images may throw {@link OutOfMemoryError} and in others
* they may exceed the texture limit for the device, which will prevent them from being rendered.
* Providing a valid size allows Glide to downsample large images, which can avoid issues with
* texture size or memory limitations. You don't have to worry about providing a size in most cases
* if you use {@link ViewTarget} so prefer {@link ViewTarget} over this class whenver possible.
*
* @see <a href="http://bumptech.github.io/glide/doc/targets.html">Glide's Target docs page</a>
* @param <Z> The type of resource that this target will receive.
* @deprecated Use {@link CustomViewTarget} if loading the content into a view, the download API if
* in the background
* (http://bumptech.github.io/glide/doc/getting-started.html#background-threads), or a {@link
* CustomTarget} for any specialized use-cases. Using {@link SimpleTarget} or {@link BaseTarget}
* is unsafe if the user does not implement {@link #onLoadCleared}, resulting in recycled
* bitmaps being referenced from the UI and hard to root-cause crashes.
*/
一个简单的Target基类,具有非基本方法的默认(通常为no-op)实现,允许调用者指定确切的宽度/高度。典型的用例看起来像这样:
目标目标=
Glide.with(片段)
.asBitmap ()
.load(“http://somefakeurl.com/fakeImage.jpeg”)
苹果(fitCenterTransform ())
.into(new SimpleTarget(250, 250)) {
@Override
public void onResourceReady(位图资源,过渡过渡){
//对bitmap做一些事情。
}
});
}
//稍后,清除Target以释放资源,防止加载队列从
//吹出比例,并改善加载时间为未来的请求:
Glide.with(片段).clear(目标);
警告!这个类很容易被误用。使用SimpleTarget仅作为最后的手段。ViewTarget或ViewTarget的子类几乎总是一个更好的选择。
不要忘记清除这个类的实例!如果你必须使用这个类,请记住,与ViewTarget不同的是,如果每个实例都更新相同的底层视图或调用者,那么反复加载到这个类的新实例中是不安全的。如果你需要加载到同一个视图或调用者反复使用这个类,总是保留一个引用前面的实例和调用RequestManager.clear(目标)在旧实例之前,开始一个新的负载或你必须重用旧的实例的负载。Glide的RequestBuilder.into(Target)方法返回您提供的目标实例,以便尽可能容易地保留对目标的引用。也就是说,你必须等到你完全完成资源调用RequestManager.clear(Target)之前,你应该总是空出对Target. onloadcleared (Drawable)中任何加载资源的引用。
在使用这个类时,始终尝试提供一个大小。只要可能,使用SimpleTarget(int, int)来处理不是Target.SIZE_ORIGINAL的值。使用目标。如果你正在加载大型图像,或者在旧的或内存受限的设备上运行应用程序,那么SIZE_ORIGINAL是不安全的,因为它会导致Glide将非常大的图像加载到内存中。在某些情况下,这些图像可能抛出OutOfMemoryError,在其他情况下,它们可能超过设备的纹理限制,这将阻止它们被渲染。提供一个有效的大小允许Glide采样较大的图像,这可以避免纹理大小或内存限制的问题。如果你使用ViewTarget,你不必担心在大多数情况下提供一个大小,所以尽可能使用ViewTarget而不是这个类。
弃用。
如果将内容加载到视图中,则使用CustomViewTarget;如果在后台(http://bumptech.github.io/glide/doc/getting-started.html#background-threads),则使用下载API;对于任何专门的用例,则使用CustomTarget。使用SimpleTarget或BaseTarget是不安全的,如果用户没有实现basettarget . onloadcleared (android.graphics.drawable.Drawable),导致回收位图被从UI引用和难以根本原因崩溃。
@Deprecated
public abstract class SimpleTarget<Z> extends BaseTarget<Z> {
private final int width;
private final int height;
/**
* Constructor for the target that uses {@link Target#SIZE_ORIGINAL} as the target width and
* height.
*/
// Public API.
@SuppressWarnings("WeakerAccess")
public SimpleTarget() {
this(SIZE_ORIGINAL, SIZE_ORIGINAL);
}
/**
* Constructor for the target that takes the desired dimensions of the decoded and/or transformed
* resource.
*
* @param width The width in pixels of the desired resource.
* @param height The height in pixels of the desired resource.
*/
// Public API.
@SuppressWarnings("WeakerAccess")
public SimpleTarget(int width, int height) {
this.width = width;
this.height = height;
}
/**
* Immediately calls the given callback with the sizes given in the constructor.
*
* @param cb {@inheritDoc}
*/
@Override
public final void getSize(@NonNull SizeReadyCallback cb) {
if (!Util.isValidDimensions(width, height)) {
throw new IllegalArgumentException(
"Width and height must both be > 0 or Target#SIZE_ORIGINAL, but given"
+ " width: "
+ width
+ " and height: "
+ height
+ ", either provide dimensions in the constructor"
+ " or call override()");
}
cb.onSizeReady(width, height);
}
@Override
public void removeCallback(@NonNull SizeReadyCallback cb) {
// Do nothing, we never retain a reference to the callback.
}
}
TargetTracker
com.bumptech.glide.manager.TargetTracker
为RequestManager保存当前活动的目标集,并转发生命周期事件。
/**
* Holds the set of {@link Target}s currently active for a {@link com.bumptech.glide.RequestManager}
* and forwards on lifecycle events.
*/
为RequestManager保存当前活动的目标集,并转发生命周期事件。
public final class TargetTracker implements LifecycleListener {
private final Set<Target<?>> targets =
Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());
public void track(@NonNull Target<?> target) {
targets.add(target);
}
public void untrack(@NonNull Target<?> target) {
targets.remove(target);
}
@Override
public void onStart() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onStart();
}
}
@Override
public void onStop() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onStop();
}
}
@Override
public void onDestroy() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onDestroy();
}
}
@NonNull
public List<Target<?>> getAll() {
return Util.getSnapshot(targets);
}
public void clear() {
targets.clear();
}
}
ThumbnailImageViewTarget
com.bumptech.glide.request.target.ThumbnailImageViewTarget
当加载多个固定尺寸的图像到ImageView时,避免额外调用View.requestLayout()。
通常,当使用RequestBuilder.thumbnail(com. bump技术。glide.requestbuilder) API加载多个图像到滚动列表的视图(如ListView, GridView,或RecyclerView)时,使用这个类是有意义的。
fixedsizerawable可能会导致倾斜或其他不良行为,这取决于你的图像、视图和缩放。如果出现这种情况,可以考虑DrawableImageViewTarget或BitmapImageViewTarget作为替代。
/**
* Avoids extra calls to {@link android.view.View#requestLayout} when loading more than once image
* into an {@link android.widget.ImageView} with fixed dimensions.
*
* <p>Typically it makes sense to use this class when loading multiple images with the {@link
* com.bumptech.glide.RequestBuilder#thumbnail(com.bumptech.glide.RequestBuilder)} API into views in
* a scrolling list like ListView, GridView, or RecyclerView.
*
* <p>{@link FixedSizeDrawable} may cause skewing or other undesirable behavior depending on your
* images, views, and scaling. If this occurs, consider {@link DrawableImageViewTarget} or {@link
* BitmapImageViewTarget} as alternatives.
*
* @param <T> The type of resource that will be displayed in the ImageView.
*/
当加载多个固定尺寸的图像到ImageView时,避免额外调用View.requestLayout()。
通常,当使用RequestBuilder.thumbnail(com. bump技术。glide.requestbuilder) API加载多个图像到滚动列表的视图(如ListView, GridView,或RecyclerView)时,使用这个类是有意义的。
fixedsizerawable可能会导致倾斜或其他不良行为,这取决于你的图像、视图和缩放。如果出现这种情况,可以考虑DrawableImageViewTarget或BitmapImageViewTarget作为替代。
// Public API.
@SuppressWarnings("WeakerAccess")
public abstract class ThumbnailImageViewTarget<T> extends ImageViewTarget<T> {
public ThumbnailImageViewTarget(ImageView view) {
super(view);
}
/** @deprecated Use {@link #waitForLayout()} insetad. */
@Deprecated
@SuppressWarnings({"deprecation"})
public ThumbnailImageViewTarget(ImageView view, boolean waitForLayout) {
super(view, waitForLayout);
}
@Override
protected void setResource(@Nullable T resource) {
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
Drawable result = getDrawable(resource);
if (layoutParams != null && layoutParams.width > 0 && layoutParams.height > 0) {
result = new FixedSizeDrawable(result, layoutParams.width, layoutParams.height);
}
view.setImageDrawable(result);
}
protected abstract Drawable getDrawable(T resource);
}
小结
Glide的模板模式使用比较复杂,这个也是业务特性决定的,模板模式的行为结构继承层次越多越复杂;当看到复杂的模板模式的时候,先不要深入业务细节。先看看抽象类和接口的业务表达,然后根据接口和抽象类的业务表达进行梳理,这样才能找到主体业务线。类似RequestManager这样实现几个接口和业务线的类,在理解上先分开单独理解解析。之后再进行整体理解。
自研产品推荐
历时一年半多开发终于smartApi-v1.0.0版本在2023-09-15晚十点正式上线
smartApi是一款对标国外的postman的api调试开发工具,由于开发人力就作者一个所以人力有限,因此v1.0.0版本功能进行精简,大功能项有:
- api参数填写
- api请求响应数据展示
- PDF形式的分享文档
- Mock本地化解决方案
- api列表数据本地化处理
- 再加上UI方面的打磨
为了更好服务大家把之前的公众号和软件激活结合,如有疑问请大家反馈到公众号即可,下个版本30%以上的更新会来自公众号的反馈。
嗯!先解释不上服务端原因,API调试工具的绝大多数时候就是一个数据模型、数据处理、数据模型理解共识的问题解决工具,所以作者结合自己十多年开发使用的一些痛点来打造的,再加上服务端开发一般是面向企业的,作者目前没有精力和时间去打造企业服务。再加上没有资金投入所以服务端开发会滞后,至于什么时候会进行开发,这个要看募资情况和用户反馈综合考虑。虽然目前国内有些比较知名的api工具了,但作者使用后还是觉得和实际使用场景不符。如果有相关吐槽也可以在作者的公众号里反馈蛤!
下面是一段smartApi使用介绍: