转载:
Android图片加载框架最全解析(一),Glide的基本用法
Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程
Android图片加载框架最全解析(三),深入探究Glide的缓存机制
Android图片加载框架最全解析(四),玩转Glide的回调与监听
Android图片加载框架最全解析(五),Glide强大的图片变换功能
Android图片加载框架最全解析(六),探究Glide的自定义模块功能
public class Glide implements ComponentCallbacks2 {
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
}
参数有两种情况,传入Application类型的参数,和传入非Application类型的参数。
Application 类型参数
Glide的生命周期就相当于绑定了整 个应用,只要应用不退出,任何时候都能够加载,
也可以理解为不对Glide生 命周期进行管理。
非Application 类型参数(Activity、Fragment、View)
会向当前的Activity当中添加一个隐藏的Fragment,用于根据页面生命周期对Glide进行管理。
Glide # with 执行过程:
假设已经初始化完毕
public class RequestManager implements LifecycleListener, ModelTypes> {
@Override
public RequestBuilder load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder asDrawable() {
return as(Drawable.class);
}
}
public class RequestBuilder implements ... {
@Override
public RequestBuilder load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
}
load() 方法 会把要加载的资源信息传入 RequestBuilder 中,返回 RequestBuilder对象
磁盘缓存线程池,默认只有一条线程
网络请求线程池,cpu核心 >= 4 则 设置为 2条线程,cpu核心 < 4 则设置为 1条线程
public class RequestBuilder implements ... {
public > Y into(@NonNull Y target) {
return into(target, /*targetListener=*/ null);
}
@NonNull
@Synthetic > Y into(...) {
return into(target, targetListener, getMutableOptions());
}
private > Y into(...) {
//必须在主线程调用,否则会泡异常
Util.assertMainThread();
...
options = options.autoClone();
//构建本次请求
Request request = buildRequest(target, targetListener, options);
//获取上次请求
Request previous = target.getRequest();
if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
//如果本次请求于上次相同,并且options配置没有设置跳过内存缓存
//回收本次请求
request.recycle();
//如果 previous 已经请求完毕,会返回false
if (!Preconditions.checkNotNull(previous).isRunning()) {
//使用 previous 去请求,如果有缓存会使用缓存
previous.begin();
}
return target;
}
//这里会回收 target 的上次请求对象
requestManager.clear(target);
//给 target 设置本次请求的 request
target.setRequest(request);
//把请求塞入队列,根据页面是否在前台,来处理请求
requestManager.track(target, request);
return target;
}
}
public class RequestManager implements LifecycleListener, ... {
//把请求塞入队列,根据页面是否在前台,来处理请求
void track(@NonNull Target target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
}
public class RequestTracker {
//请求队列
private final Set requests = Collections.newSetFromMap(new WeakHashMap());
public void runRequest(@NonNull Request request) {
//把请求塞入队列,
requests.add(request);
if (!isPaused) {//如果页面未暂停,直接调用 begin()开始请求
request.begin();
} else {
//页面暂停,请求塞入 pendingRequests 队列
pendingRequests.add(request);
}
}
}
public final class SingleRequest implements Request, ... {
...
//图片加载引擎
private Engine engine;
//Request 默认有一个对象池
private static final Pools.Pool> POOL = FactoryPools.simple(150,
new FactoryPools.Factory>() {
@Override
public SingleRequest create() {
return new SingleRequest
活动缓存:HashMap value为弱引用,存放正在使用的图片。图片不再使用时会移除并添加到内存缓存
内存缓存:LruCache LRU算法淘汰,key为 EntryKey 与图片请求配置相关
磁盘缓存:实现 LRU 算法淘汰,文件名 为 EntryKey 使用SHA2算法得到,与图片请求配置相关
LruBitmapPool:Bitmap对象池,实现 LRU算法淘汰,Glide中主要用于图像变换后Bitmap对象的复用,避免频繁gc
Request 对象池:默认 150个,对象复用,避免频繁gc
DecodeJob 对象池:默认大小 150,对象复用,避免频繁gc
EngineJob 对象池:默认大小 150,对象复用,避免频繁gc
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);
内部实际上就是 new EngineKey(…),
EngineKey 类重写了 hashCode()、toString(),确保只有传入EngineKey的参数完全一直才能匹配。
采用 HashMap,value为图片 弱引用 缓存。
活动缓存通过引用计数,来表示图片是否在使用中。
如果计数为0,表示不再使用,图片会从 活动缓存 中删除,放到内存缓存中。
【由于内存缓存使用的 LRU算法 淘汰缓存,
正在使用的缓存放到活动缓存中,不在内存缓存,不会被LRU算法淘汰。】
1)活动缓存存储结构
final class ActiveResources {
final Map activeEngineResources = new HashMap<>();
}
2)活动缓存的存入
class EngineJob implements DecodeJob.Callback, Poolable {
private EngineResource engineResource;
@Override
public void onResourceReady(Resource resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
private static class MainThreadCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message message) {
EngineJob job = (EngineJob) message.obj;
switch (message.what) {
case MSG_COMPLETE:
job.handleResultOnMainThread();
break;
...
}
return true;
}
}
@Synthetic
void handleResultOnMainThread() {
...
engineResource = engineResourceFactory.build(resource, isCacheable);
...
//引用计数+1,表示EngineJob 正在使用该图片
engineResource.acquire();
//Engine.onEngineJobComplete()中,把图片存入 活动缓存 activeResources 中
listener.onEngineJobComplete(this, key, engineResource);
for (int i = 0, size = cbs.size(); i < size; i++) {
ResourceCallback cb = cbs.get(i);
if (!isInIgnoredCallbacks(cb)) {
//每一个地方使用到图片,引用计数都会 +1
engineResource.acquire();
cb.onResourceReady(engineResource, dataSource);
}
}
/* 引用计数-1,表示 EngineJob 对该图片使用已经结束
* 当引用计数 为0 时,表示图片不在使用中,会从 活动缓存 中删除。
*/
engineResource.release();
release(false /*isRemovedFromQueue*/);
}
}
3)活动缓存的删除
引用计数为 0 时,会调用 Engine.onResourceReleased()把图片从 活动缓存 删除,
并放入内存缓存。
采用 LruCache LRU最近最少使用 算法,实现内存缓存。
低内存时,会自动 减半 / 清空 内存缓存
public class LruResourceCache extends LruCache> implements MemoryCache {
//低内存时,会自动 减半/清空 内存缓存
@Override
public void trimMemory(int level) {
if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
clearMemory();
} else if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
trimToSize(getMaxSize() / 2);
}
}
}
DiskCacheStrategy.NONE 表示 不缓存 任何内容
DiskCacheStrategy.DATA 表示只 缓存原始图片
DiskCacheStrategy.RESOURCE 表示 只缓存转换过后的图片
DiskCacheStrategy.ALL 表示 既缓存原始图片,也缓存转换过后的图片
DiskCacheStrategy.AUTOMATIC 表示让Glide根据图片资源 智能地选择 使用哪一种缓存策略(默认选项)
磁盘缓存也使用 LRU算法,DiskLruCache
com.bumptech.glide.disklrucache.DiskLruCache
内部也是维护 LinkedHashMap,实现 LRU算法淘汰超过上限的缓存。
1)磁盘缓存写入
图片从网络下载、解码成功后,会先写入 活动缓存,然后写入 磁盘缓存。
class DecodeJob implements Runnable, ... {
//图片下载完成后回调
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher fetcher,
DataSource dataSource, Key attemptedKey) {
...
decodeFromRetrievedData();
...
}
private void decodeFromRetrievedData() {
...
Resource resource = null;
try {
//图片解码
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
...
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private void notifyEncodeAndRelease(Resource resource, DataSource dataSource) {
...
//这里会把图片添加到 活动缓存 中
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
//这里会把图片写入磁盘
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
...
}
...
}
private static class DeferredEncodeManager {
private Key key;
private ResourceEncoder encoder;
private LockedResource toEncode;
...
//写入磁盘
void encode(DiskCacheProvider diskCacheProvider, Options options) {
TraceCompat.beginSection("DecodeJob.encode");
try {
//写入磁盘
diskCacheProvider.getDiskCache().put(key, new DataCacheWriter<>(encoder, toEncode, options));
} finally {
toEncode.unlock();
TraceCompat.endSection();
}
}
...
}
}
2)磁盘缓存 的加载
从前面工作流程中可知,图片的加载真正执行是在 DecodeJob.run() 中
class DecodeJob implements Runnable, ... {
//判断是否读取磁盘缓存
boolean willDecodeFromCache() {
Stage firstStage = getNextStage(Stage.INITIALIZE);
return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
}
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE://默认情况
/* 从缓存策略的实现可知,除了 DiskCacheStrategy.NONE 之外
* 其他所有策略 decodeCachedResource()/decodeCachedData() 返回值都是 true
* 所以这里默认会返回 Stage.RESOURCE_CACHE/Stage.DATA_CACHE
*/
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
...
}
}
@Override
public void run() {
...
runWrapped();
...
}
private void runWrapped() {
switch (runReason) {// runReason 默认值是 RunReason.INITIALIZE;
case INITIALIZE:
//返回 Stage.RESOURCE_CACHE
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
...
}
}
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
...
}
}
}
class ResourceCacheGenerator implements ... {
@Override
public boolean startNext() {
...
currentKey =
new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
//这里会调用 DiskLruCache 根据 key 读取磁盘缓存
cacheFile = helper.getDiskCache().get(currentKey);
...
return started;
}
}
Bitmap 对象池,避免频繁的创建以及回收Bitmap对象,进而减少GC的出现。
内部实现类 LRU算法,用于缓存超过默认上限时,淘汰策略。
Bitmap类型的Resource,在回收时,会把图片放入 BitmapPool 对象池中,用于复用
从 Glide 内部的调用看,主要用于 BitmapTransformation 变换后的图片复用 Bitmap 对象
LruBitmapPool 容量:
分辨率宽 * 分辨率高 * 4字节(ARGB_8888一个像素点大小) * 比例(8.0以下是 4,8.0及以上是 1)
static final int BITMAP_POOL_TARGET_SCREENS =
Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? 4 : 1;
@Synthetic float bitmapPoolScreens = BITMAP_POOL_TARGET_SCREENS;
int screenSize = widthPixels * heightPixels * BYTES_PER_ARGB_8888_PIXEL;
int targetBitmapPoolSize = Math.round(screenSize * builder.bitmapPoolScreens);
Bitmap类型的Resource回收时,把 Bitmap 放入对象池
public class BitmapResource implements Resource, ... {
@Override
public void recycle() {
//把转换后的图片,保存到 LruBitmapPool 中
bitmapPool.put(bitmap);
}
}
public class BitmapDrawableResource extends DrawableResource ... {
@Override
public void recycle() {
//把转换后的图片,保存到 LruBitmapPool 中
bitmapPool.put(drawable.getBitmap());
}
}
public class LruBitmapPool implements BitmapPool {
@Override
public synchronized void put(Bitmap bitmap) {
...
final int size = strategy.getSize(bitmap);
strategy.put(bitmap);
tracker.add(bitmap);
puts++;
currentSize += size;
...
//计算是否超出maxSize,超出则进行 LRU回收
evict();
}
private void evict() {
trimToSize(maxSize);
}
@Override
@NonNull
public Bitmap get(int width, int height, Bitmap.Config config) {
//从 BitmapPool 中根据尺寸和配置获取 Bitmap对象。
Bitmap result = getDirtyOrNull(width, height, config);
if (result != null) {
//找到匹配的Bitmap,擦除图片的像素值
result.eraseColor(Color.TRANSPARENT);
} else {
//没找到匹配的Bitmap,则创建新的Bitmap
result = createBitmap(width, height, config);
}
return result;
}
}
在调用LruBitmapPool.get方法获取到Bitmap后,
通过如下方法将获取到的 bitmap作为参数 传给Canvas,
在canvas中把 inBitmap 像素填充到进去,实现对象复用
public final class TransformationUtils {
private static void applyMatrix(@NonNull Bitmap inBitmap, @NonNull Bitmap targetBitmap, Matrix matrix) {
BITMAP_DRAWABLE_LOCK.lock();
try {
Canvas canvas = new Canvas(targetBitmap);
canvas.drawBitmap(inBitmap, matrix, DEFAULT_PAINT);
clear(canvas);
} finally {
BITMAP_DRAWABLE_LOCK.unlock();
}
}
}
Glide 图片转换库 glide-transformations:https://github.com/wasabeef/glide-transformations
Glide 图片的变换会在 图片解码后 执行
class DecodeJob implements Runnable, Poolable, ... {
Resource onResourceDecoded(DataSource dataSource, Resource decoded) {
...
Transformation appliedTransformation = null;
Resource transformed = decoded;
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
//执行图片转换
transformed = appliedTransformation.transform(glideContext, decoded, width, height);
}
...
return result;
}
}
public class CenterCrop extends BitmapTransformation {
...
/* 参数一:BitmapPool Bitmap缓存池,用于对Bitmap对象进行重用
* 否则每次图片变换都要重新创建Bitmap对象,会导致频繁 内存抖动/GC
*
* 参数二:toTransform 原始图片的Bitmap对象,我们就是要对它来进行图片变换。
*
* 参数三/四: 图片变换后的 宽度 和 高度
*/
@Override
protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
return TransformationUtils.centerCrop(pool, toTransform, outWidth, outHeight);
}
...
}
public final class TransformationUtils {
public static Bitmap centerCrop(BitmapPool pool, Bitmap inBitmap, int width, int height) {
if (inBitmap.getWidth() == width && inBitmap.getHeight() == height) {
return inBitmap;
}
//通过I mageView尺寸 与 原图 比例,进行缩放
final float scale;
final float dx;
final float dy;
Matrix m = new Matrix();
if (inBitmap.getWidth() * height > width * inBitmap.getHeight()) {
scale = (float) height / (float) inBitmap.getHeight();
dx = (width - inBitmap.getWidth() * scale) * 0.5f;
dy = 0;
} else {
scale = (float) width / (float) inBitmap.getWidth();
dx = 0;
dy = (height - inBitmap.getHeight() * scale) * 0.5f;
}
m.setScale(scale, scale);
m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
Bitmap result = pool.get(width, height, getNonNullConfig(inBitmap));
// We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given.
TransformationUtils.setAlpha(inBitmap, result);
applyMatrix(inBitmap, result, m);
return result;
}
/* inBitmap 一般是原图,一般会在 活动缓存/内存缓存中
* targetBitmap 是尝试从 BitmapPool 对象池中拿的 Bitmap,用于生成变换后图片
* 这里把 inBitmap 图像绘制到 targetBitmap上。
*/
private static void applyMatrix(Bitmap inBitmap, Bitmap targetBitmap, Matrix matrix) {
BITMAP_DRAWABLE_LOCK.lock();
try {
Canvas canvas = new Canvas(targetBitmap);
canvas.drawBitmap(inBitmap, matrix, DEFAULT_PAINT);
clear(canvas);
} finally {
BITMAP_DRAWABLE_LOCK.unlock();
}
}
}
1)继承 BitmapTransformation
2)重写 transform() 方法
public class CircleCrop extends BitmapTransformation {
...
@Override
public String getId() {
return "com.example.test.CircleCrop";
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
int diameter = Math.min(toTransform.getWidth(), toTransform.getHeight());
//尝试从BitmapPool 中获取目标Bitmap对象
final Bitmap toReuse = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888);
final Bitmap result;
if (toReuse != null) {
result = toReuse;
} else {
//对象池中没找到,则创建一个新的 Bitmap
result = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);
}
//获取 裁剪后图片 相对与 原图 在 x轴、y轴,离原点的距离dx、dy
int dx = (toTransform.getWidth() - diameter) / 2;
int dy = (toTransform.getHeight() - diameter) / 2;
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
BitmapShader shader = new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP,
BitmapShader.TileMode.CLAMP);
if (dx != 0 || dy != 0) {
Matrix matrix = new Matrix();
/* 把裁剪后的图片,移到动到原点
* 图片以图片更短的一遍为标准裁剪成正方形后,长的那边的原点已经不在原点上了。
*/
matrix.setTranslate(-dx, -dy);
shader.setLocalMatrix(matrix);
}
paint.setShader(shader);
paint.setAntiAlias(true);
//半径
float radius = diameter / 2f;
//画圆
canvas.drawCircle(radius, radius, radius, paint);
//画完后,尝试把复用的 Bitmap 放回 Bitmap缓存池
if (toReuse != null && !pool.put(toReuse)) {
//放入缓存池失败,回收
toReuse.recycle();
}
return result;
}
}
public class RequestBuilder implements ... {
public ViewTarget into(@NonNull ImageView view) {
...
/* buildImageViewTarget()方法:根据 transcodeClass 的类型,构建一个 ViewTarget 对象
* BitmapImageViewTarget / DrawableImageViewTarget
*/
return into(glideContext.buildImageViewTarget(view, transcodeClass), null, requestOptions);
}
//这里会触发请求加载图片(活动缓存/内存缓存/磁盘缓存/网络)
private > Y into(@NonNull Y target, ...) {
...
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
}
public class ImageViewTargetFactory {
//据传入的class参数,构建一个 ViewTarget 对象
public ViewTarget buildTarget(ImageView view, Class clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
public final class SingleRequest implements Request, ResourceCallback, ... {
private Target target;
...
private void onResourceReady(Resource resource, R result, DataSource dataSource) {
...
if ((requestListener == null
|| !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource))
&& (targetListener == null
|| !targetListener.onResourceReady(result, model, target, dataSource, isFirstResource))) {
Transition animation = animationFactory.build(dataSource, isFirstResource);
/* 最终会触发 target.onResourceReady()
* BitmapImageViewTarget / DrawableImageViewTarget 中,
* 会调用view.setImageBitmap(resource) / view.setImageDrawable(resource)
* 把图片展示在控件上。
*/
target.onResourceReady(result, animation);
}
...
notifyLoadSuccess();
}
}
GlideApp.with(config.getContext()).load(thumbnailUrl).preload();
通过 PreloadTarget 实现
public class RequestBuilder implements ... {
public Target preload(int width, int height) {
final PreloadTarget target = PreloadTarget.obtain(requestManager, width, height);
return into(target);
}
/**
*
*/
public Target preload() {
return preload(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
}
public Target preload(int width, int height) {
final PreloadTarget target = PreloadTarget.obtain(requestManager, width, height);
return into(target);
}
}
public final class PreloadTarget extends SimpleTarget {
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;
}
});
...
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition transition) {
HANDLER.obtainMessage(MESSAGE_CLEAR, this).sendToTarget();
}
@SuppressWarnings("WeakerAccess")
@Synthetic void clear() {
//图片下载完成后,移除 BaseTarget.request,释放资源
requestManager.clear(this);
}
}
仅仅下载图片,不加载到 内存
Glide.with(GlideManager.getApp())
.downloadOnly()
.load(url)
.into(new SimpleTarget() {
@Override
public void onResourceReady(File resource, Transition transition) {
//返回下载的文件 resource
}
@Override
public void onLoadFailed(Drawable errorDrawable) {
getter.onFail();
}
});
源码:
public class RequestBuilder implements ... {
protected static final RequestOptions DOWNLOAD_ONLY_OPTIONS =
new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.DATA)
.priority(Priority.LOW)
.skipMemoryCache(true);//不加载到内存,仅仅是通过这个配置而已
//只能在主线程调用
public > Y downloadOnly(@NonNull Y target) {
return getDownloadOnlyRequest().into(target);
}
/* 该方法内部调用 submit()的作用:
* 如果当前线程在子线程,会把下载的请求放到主线程去做,仅此而已。
* 在主线程也可以调用。
*/
public FutureTarget downloadOnly(int width, int height) {
return getDownloadOnlyRequest().submit(width, height);
}
protected RequestBuilder getDownloadOnlyRequest() {
return new RequestBuilder<>(File.class, this).apply(DOWNLOAD_ONLY_OPTIONS);
}
@NonNull
public FutureTarget submit(int width, int height) {
final RequestFutureTarget target =
new RequestFutureTarget<>(glideContext.getMainHandler(), width, height);
//如果调用线程不是主线程,则把请求塞到主线程的Handler 的 MessageQueue中。
if (Util.isOnBackgroundThread()) {
glideContext.getMainHandler().post(new Runnable() {
@Override
public void run() {
if (!target.isCancelled()) {
into(target, target);
}
}
});
} else {
into(target, target);
}
return target;
}
}
Glide.with(this)
.load(url)
.listener(new RequestListener() {
@Override
public boolean onException(...) {
/* 返回false就表示这个事件没有被处理,还会继续向下传递,
* 返回true就表示这个事件已经被处理掉了,从而不会再继续向下传递
*/
return false;
}
@Override
public boolean onResourceReady(...) {
/* 返回false就表示这个事件没有被处理,还会继续向下传递,
* 返回true就表示这个事件已经被处理掉了,从而不会再继续向下传递
*/
return false;
}
}).into(imageView);
源码:
public final classSingleRequest implements Request, ... {
private RequestListener requestListener;
private void onResourceReady(Resource resource, R result, DataSource dataSource) {
...
/* 这里会回调 requestListener.onResourceReady()
* 返回false的时候,才会继续调用Target的onResourceReady()方法
*/
if ((requestListener == null
|| !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource))
&& (targetListener == null
|| !targetListener.onResourceReady(result, model, target, dataSource, isFirstResource))) {
Transition animation =
animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
...
notifyLoadSuccess();
}
}
思路:拦截器,获取请求返回的数据 ResponseBody,计算返回的数据百分比,回调到调用层
@GlideModule
public class UnsafeOkHttpGlideModule extends AppGlideModule {
...
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
//添加拦截器到Glide
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addInterceptor(new ProgressInterceptor());
OkHttpClient okHttpClient = builder.build();
//原来的是 new OkHttpUrlLoader.Factory();
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient));
}
@Override
public boolean isManifestParsingEnabled() {
return false;//false :不再解析AndroidMenifest文件
}
}
下载进度 拦截器
public class ProgressInterceptor implements Interceptor {
public static final Map LISTENER_MAP = new HashMap<>();
//入注册下载监听
public static void addListener(String url, ProgressListener listener) {
LISTENER_MAP.put(url, listener);
}
//取消注册下载监听
public static void removeListener(String url) {
LISTENER_MAP.remove(url);
}
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
String url = request.url().toString();
ResponseBody body = response.body();
Response newResponse = response.newBuilder().body(new ProgressResponseBody(url, body)).build();
return newResponse;
}
}
//下载进度计算与回调
public class ProgressResponseBody extends ResponseBody {
private static final String TAG = "XGlide";
private BufferedSource bufferedSource;
private ResponseBody responseBody;
private ProgressListener listener;
public ProgressResponseBody(String url, ResponseBody responseBody) {
this.responseBody = responseBody;
//从 拦截器中 传进来的 进度监听起
listener = ProgressInterceptor.LISTENER_MAP.get(url);
}
@Nullable
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(new ProgressSource(responseBody.source()));
}
return bufferedSource;
}
private class ProgressSource extends ForwardingSource {
long totalBytesRead = 0;
int currentProgress;
ProgressSource(Source source) {
super(source);
}
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
long fullLength = responseBody.contentLength();
if (bytesRead == -1) {
totalBytesRead = fullLength;
} else {
totalBytesRead += bytesRead;
}
int progress = (int) (100f * totalBytesRead / fullLength);
Log.d(TAG, "download progress is " + progress);
//下载进度回调
if (listener != null && progress != currentProgress) {
listener.onProgress(progress);
}
if (listener != null && totalBytesRead == fullLength) {
listener = null;
}
currentProgress = progress;
return bytesRead;
}
}
}
//进度监听回调
public interface ProgressListener {
void onProgress(int progress);
}
使用:
public void testProgressLoad() {
ProgressInterceptor.addListener(url, new ProgressListener() {
@Override
public void onProgress(int progress) {
//进度回调
}
});
RequestOptions options = new RequestOptions()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(this)
.load(url)
.apply(options)
.listener(new RequestListener() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
//加载结束后,移除进度监听器,避免内存泄露
ProgressInterceptor.removeListener(url);
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
//加载结束后,移除进度监听器,避免内存泄露
ProgressInterceptor.removeListener(url);
return false;
}
}).into(img);
}
public enum Priority {
IMMEDIATE, //立即执行(最高优先级)
HIGH, //高优先级
NORMAL, //普通优先级
LOW, //低优先级
}
使用:
RequestOptions options = new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.DATA)
.priority(Priority.LOW);
使用Matrix类的时候,图片的移动和旋转,只能在控件上面。
控件本身没有动,控件里面的内容动了
1)setTranslate(float ds,float dy)
控制 Matrix 进行平移
2)setSkew(float kx ,float ky ,float px ,float py)
控制 Matrix 以 px、py 为轴心进行倾斜。kx、ky为X、Y方向上的倾斜距离。
3)setSkew(float kx,float ky)
控制Matrix进行倾斜。kx、ky为X、Y方向上的倾斜距离。
4)setRotate(float degrees)
控制Matrix进行旋转,degrees控制旋转的角度。
5)setRotate(float degrees,float px,float py)
设置以px、py为轴心进行旋转,degrees控制旋转的角度。
6)setScale(float sx,float sy)
设置Matrix进行缩放,sx、sy控制X、Y方向上的缩放比例。
7)setScale(float sx,float sy,float px,float py)
设置Matrix以px、py为轴心进行缩放,sx、sy控制X、Y方向上的缩放比例。
例一:
Matrix m = new Matrix();
m.setRotate(45);
m.setTranslate(80, 80);
只有m.setTranslate(80, 80)有效,因为m.setRotate(45);被清除.
例子二:
Matrix m = new Matrix();
m.preScale(2f,2f);
m.preTranslate(50f, 20f);
m.postScale(0.2f, 0.5f);
m.postTranslate(20f, 20f);
执行顺序:m.preTranslate(50f, 20f)–>m.preScale(2f,2f)–>m.postScale(0.2f, 0.5f)–>m.postTranslate(20f, 20f)
注意:m.preTranslate(50f, 20f)比m.preScale(2f,2f)先执行,因为它插到了队列的最前端.
例子五:
Matrix m = new Matrix();
m.postTranslate(20, 20);
m.preScale(0.2f, 0.5f);
m.setScale(0.8f, 0.8f);
m.postScale(3f, 3f);
m.preTranslate(0.5f, 0.5f);
执行顺序:m.preTranslate(0.5f, 0.5f)–>m.setScale(0.8f, 0.8f)–>m.postScale(3f, 3f)
注意:m.setScale(0.8f, 0.8f)清除了前面的m.postTranslate(20, 20)和m.preScale(0.2f, 0.5f);