Android框架-Glide 4.6

转载:
Android图片加载框架最全解析(一),Glide的基本用法
Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程
Android图片加载框架最全解析(三),深入探究Glide的缓存机制
Android图片加载框架最全解析(四),玩转Glide的回调与监听
Android图片加载框架最全解析(五),Glide强大的图片变换功能
Android图片加载框架最全解析(六),探究Glide的自定义模块功能

一、Glide的执行流程

活动缓存中未找到
内存缓存中未找到
磁盘缓存中未找到
获取成功
获取成功
获取成功
构建 Glide.with
得到RequestManager
RequestManager.load
得到 RequestBuilder
一系列imgUrl 等配置...
RequestBuilder.into
活动缓存取使用中图片
内存缓存
磁盘缓存获取
网络流获取
解码
图片转换 Transformation
放入 活动缓存 磁盘缓存
onSizeReady
1. Glide.with(args) 得到RequestManager
public class Glide implements ComponentCallbacks2 {
	@NonNull
	public static RequestManager with(@NonNull Context context) {
		return getRetriever(context).get(context);
	}
}

参数有两种情况,传入Application类型的参数,和传入非Application类型的参数。

  1. Application 类型参数
    Glide的生命周期就相当于绑定了整 个应用,只要应用不退出,任何时候都能够加载,
    也可以理解为不对Glide生 命周期进行管理

  2. 非Application 类型参数(Activity、Fragment、View)
    会向当前的Activity当中添加一个隐藏的Fragment,用于根据页面生命周期对Glide进行管理。

Glide # with 执行过程:
假设已经初始化完毕

RequestManagerRetriever # get
从context 参数获取 Activity
得到 Activity # FragmentManager
创建一个无UI 的Fragment
RequestManagerFragment
RequestManagerFragment内部创建
一个ActivityFragmentLifecycle
创建RequestManager 实现 LifecycleListener
将这个RequestManager注册到
ActivityFragmentLifecycle 中
RequestManagerFragment
假设执行 onStart
内部变量 lifeCycle 执行 onStart
部变量 lifecycleListener 执行 onStart
RequestManager 执行 resumeRequests
2. RequestManager.load(Object)返回 RequestBuilder
public class RequestManager implements LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
	
	@Override
	public RequestBuilder<Drawable> load(@Nullable String string) {
		return asDrawable().load(string);
	}
	
	public RequestBuilder<Drawable> asDrawable() {
		return as(Drawable.class);
	}
}

public class RequestBuilder<TranscodeType> implements ... {
	
	@Override
	public RequestBuilder<TranscodeType> load(@Nullable String string) {
		return loadGeneric(string);
	}
	
	private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
		this.model = model;
		isModelSet = true;
		return this;
	}
}

load() 方法 会把要加载的资源信息传入 RequestBuilder 中,返回 RequestBuilder对象

3. RequestBuilder.into(target)
RequestBuilder.into(target)执行过程:
  • 1)该target 上次请求与本次是否相同(宽高、配置),相同则复用上次请求对象,
    否则从对象池(容量150)获取 Request 用于请求
  • 2)校验要获取的图片宽高
  • 3)分别从 活动缓存<HashMap,弱引用存储> / 内存缓存<LruCache> / 磁盘缓存<DiskLruCache> 去取
  • 4)无缓存,则通过 线程池 网络请求下载图片
  • 5)下载完成后,解码
  • 6)把图片添加到 活动缓存、磁盘缓存 中,
    然后使用 引用计数,当图片不再使用时,从活动缓存删除并添加到内存缓存

    (内存缓存使用了 LRU算法 淘汰缓存,正在使用图片放到活动缓存中,不在内存缓存,不会被LRU算法淘汰)
  • 7)回调 onResourceReady()
本次请求配置与 previous 匹配
使用 previous 请求
本次请求配置与 previous 不匹配
使用新的 Request
活动缓存没找到
内存缓存没找到
获取成功
获取成功
获取失败
获取成功
RequestBuilder # into
对象池获取 Request
Request对象池 大小为 150
Request previous = target.getRequest
获取 target 的上次请求
RequestManager # track
参数为 Request对象
SingleRequest # begin
宽高可用
调用 SingleRequest # onSizeReady
Engine # load
构建 EngineKey
Engine # loadFromActiveResources
从活动缓存找
活动缓存是 HashMap 以弱引用方式存
Engine # loadFromCache
内存缓存找
LruCache
构建 EngineJob
构建 DecodeJob 实现了Runnable
ResourceCallback # onResourceReady
EngineJob # start
参数为 DecodeJob
线程池 执行 DecodeJob#run
磁盘缓存获取
DataFetcher # loadData 同步网络请求
DecodeJob # decodeFromFetcher 解码
EngineJob # onResourceReady
EngineJob # handleResultOnMainThread
把图片添加到 活动缓存
把图片添加到 磁盘缓存 中

EngineResource # acquire 引用计数
图片不再使用时 从活动缓存删除并添加到内存缓存

磁盘缓存线程池,默认只有一条线程
网络请求线程池,cpu核心 >= 4 则 设置为 2条线程,cpu核心 < 4 则设置为 1条线程

public class RequestBuilder<TranscodeType> implements ... {
	
	public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
		return into(target, /*targetListener=*/ null);
	}

	@NonNull
	@Synthetic <Y extends Target<TranscodeType>> Y into(...) {
		return into(target, targetListener, getMutableOptions());
	}

	private <Y extends Target<TranscodeType>> 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<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
	
	public void runRequest(@NonNull Request request) {
		//把请求塞入队列,
		requests.add(request);
		
		if (!isPaused) {//如果页面未暂停,直接调用 begin()开始请求
			request.begin();
		} else {
			//页面暂停,请求塞入 pendingRequests 队列
			pendingRequests.add(request);
		}
	}
}

public final class SingleRequest<R> implements Request, ... {
	...
	//图片加载引擎
	private Engine engine;
	
	//Request 默认有一个对象池
	private static final Pools.Pool<SingleRequest<?>> POOL = FactoryPools.simple(150,
		
		new FactoryPools.Factory<SingleRequest<?>>() {
			@Override
			public SingleRequest<?> create() {
			  return new SingleRequest<Object>();
			}
	});
	
	@Override
	public void begin() {
		...
		//图片尺寸有效
		if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
			//这里是真正去请求
			onSizeReady(overrideWidth, overrideHeight);
		} else {
			//这里会去获取图片尺寸,然后回调 onSizeReady()去加载图片
			target.getSize(this);
		}
		...
	}
	
	@Override
	public void onSizeReady(int width, int height) {
		...
		//调用 Engine.load()加载
		loadStatus = engine.load(
			glideContext,
			model,
			requestOptions.getSignature(),
			this.width,
			this.height,
			requestOptions.getResourceClass(),
			transcodeClass,
			priority,
			requestOptions.getDiskCacheStrategy(),
			requestOptions.getTransformations(),
			requestOptions.isTransformationRequired(),
			requestOptions.isScaleOnlyOrNoTransform(),
			requestOptions.getOptions(),
			requestOptions.isMemoryCacheable(),
			requestOptions.getUseUnlimitedSourceGeneratorsPool(),
			requestOptions.getUseAnimationPool(),
			requestOptions.getOnlyRetrieveFromCache(),
			this);
		...
	}			
}

public class Engine implements EngineJobListener, ... {
	
	/* 弱引用实现 活动缓存
	 * activeResources 是Glide通过引用计数实现 Bitmap复用的关键
	 */
	private final ActiveResources activeResources;
	
	/* 内存缓存 LruCache 实现
	 * LruCache 内部是一个 LinkedHashMap,使用LRU 最近最少访问算法,淘汰缓存
	 */
	private final MemoryCache cache;
	
	private final LazyDiskCacheProvider diskCacheProvider;//磁盘缓存
	
	public <R> LoadStatus load(...) {
		//到这里还是主线程
		Util.assertMainThread();
		/* 根据请求信息(包括load(model)方法的参数、图片宽高等),构建 key
		 * 这个key,也是用于 memoryCache  diskCache 的key
		 */
		EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
			resourceClass, transcodeClass, options);
		
		//从 活动缓存 activeResources 去拿图片
		EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
		if (active != null) {
			//从活动缓存中找到图片后,回调 onResourceReady() 方法	
			cb.onResourceReady(active, DataSource.MEMORY_CACHE);
			...
			return null;
		}
		/* 活动缓存 activeResources 中没找到图片
		 * 去内存缓存 cache 中获取图片
		 */
		EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
		if (cached != null) {
			//从内存缓存中找到图片后,回调 onResourceReady() 方法	
			cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
			...
			return null;
		}
		//根据key 获取 EngineJob
		EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
		if (current != null) {
			current.addCallback(cb);
			...
			return new LoadStatus(cb, current);
		}
		//构建 EngineJob
		EngineJob<R> engineJob =
			engineJobFactory.build(
				key,
				isMemoryCacheable,
				useUnlimitedSourceExecutorPool,
				useAnimationPool,
				onlyRetrieveFromCache);
		//构建 DecodeJob
		DecodeJob<R> decodeJob =
			decodeJobFactory.build(
				glideContext,
				model,
				key,
				signature,
				width,
				height,
				resourceClass,
				transcodeClass,
				priority,
				diskCacheStrategy,
				transformations,
				isTransformationRequired,
				isScaleOnlyOrNoTransform,
				onlyRetrieveFromCache,
				options,
				engineJob);
		//根据key 缓存 EngineJob
		jobs.put(key, engineJob);
		engineJob.addCallback(cb);
		
		engineJob.start(decodeJob);
		...
		return new LoadStatus(cb, engineJob);
	}			
}

class EngineJob<R> implements DecodeJob.Callback<R>, Poolable {
	//EngineJob 对象池
	private final Pools.Pool<EngineJob<?>> pool;
	
	// 磁盘缓存线程池,默认只有一条核心线程、最大线程数也是一,闲置时间是0
	private final GlideExecutor diskCacheExecutor;
	
	//核心线程为 0,最大线程数为 Integer.MAX_VALUE,闲置时间 10秒
	private final GlideExecutor sourceUnlimitedExecutor;
	
	/* 根据cpu核心数定义线程数
	 *	cpu核心 >= 4 则 设置为 2条线程,cpu核心 < 4 则设置为 1条线程
	 */
	private final GlideExecutor sourceExecutor;
	
	/* 根据cpu核心数定义线程数
	 *	cpu核心 >= 4 则 设置为 2条线程,cpu核心 < 4 则设置为 1条线程
	 */
	private final GlideExecutor animationExecutor;
	
	public void start(DecodeJob<R> decodeJob) {
		this.decodeJob = decodeJob;
		//根据是否使用缓存、以及配置,获取线程池
		GlideExecutor executor = decodeJob.willDecodeFromCache()
			? diskCacheExecutor
			: getActiveSourceExecutor();
			//最终获取图片的行为在 DecodeJob.run() 中
			executor.execute(decodeJob);
	}
}

class DecodeJob<R> implements Runnable, ... {
	//DecodeJob 对象池
	private final Pools.Pool<DecodeJob<?>> pool;
	
	@Override
	public void run() {
		...
	}		
}

二、Glide的缓存机制

活动缓存HashMap value为弱引用,存放正在使用的图片。图片不再使用时会移除并添加到内存缓存

内存缓存:LruCache LRU算法淘汰,key为 EntryKey 与图片请求配置相关

磁盘缓存:实现 LRU 算法淘汰,文件名 为 EntryKey 使用SHA2算法得到,与图片请求配置相关

LruBitmapPoolBitmap对象池,实现 LRU算法淘汰,Glide中主要用于图像变换后Bitmap对象的复用,避免频繁gc

Request 对象池默认 150个,对象复用,避免频繁gc

DecodeJob 对象池默认大小 150,对象复用,避免频繁gc

EngineJob 对象池默认大小 150,对象复用,避免频繁gc

1、缓存 key
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);

内部实际上就是 new EngineKey(…),
EngineKey 类重写了 hashCode()、toString(),确保只有传入EngineKey的参数完全一直才能匹配。

2、活动缓存,存放正在使用的图片

采用 HashMap,value为图片 弱引用 缓存。

活动缓存通过引用计数,来表示图片是否在使用中。
如果计数为0,表示不再使用,图片会从 活动缓存 中删除,放到内存缓存中。

【由于内存缓存使用的 LRU算法 淘汰缓存,
正在使用的缓存放到活动缓存中,不在内存缓存,不会被LRU算法淘汰。】

1)活动缓存存储结构

final class ActiveResources {
	final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
}

2)活动缓存的存入

class EngineJob<R> implements DecodeJob.Callback<R>, Poolable {
	
	private EngineResource<?> engineResource;
	
	@Override
	public void onResourceReady(Resource<R> 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()把图片从 活动缓存 删除,
并放入内存缓存。

3、内存缓存

采用 LruCache LRU最近最少使用 算法,实现内存缓存。
低内存时,会自动 减半 / 清空 内存缓存

public class LruResourceCache extends LruCache<Key, Resource<?>> 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);
		}
	}
}
4、磁盘缓存

DiskCacheStrategy.NONE 表示 不缓存 任何内容
DiskCacheStrategy.DATA 表示只 缓存原始图片
DiskCacheStrategy.RESOURCE 表示 只缓存转换过后的图片
DiskCacheStrategy.ALL 表示 既缓存原始图片,也缓存转换过后的图片
DiskCacheStrategy.AUTOMATIC 表示让Glide根据图片资源 智能地选择 使用哪一种缓存策略(默认选项

磁盘缓存也使用 LRU算法,DiskLruCache
com.bumptech.glide.disklrucache.DiskLruCache
内部也是维护 LinkedHashMap,实现 LRU算法淘汰超过上限的缓存。

1)磁盘缓存写入
图片从网络下载、解码成功后,会先写入 活动缓存,然后写入 磁盘缓存

class DecodeJob<R> implements Runnable, ... {
	
	//图片下载完成后回调
	@Override
	public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
	  DataSource dataSource, Key attemptedKey) {
		...
		decodeFromRetrievedData();
		...
	}

	private void decodeFromRetrievedData() {
		...
		Resource<R> resource = null;
		try {
			//图片解码
			resource = decodeFromData(currentFetcher, currentData, currentDataSource);
		} catch (GlideException e) {
			...
		}
		if (resource != null) {
		  notifyEncodeAndRelease(resource, currentDataSource);
		} else {
		  runGenerators();
		}
	}

	private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
		...
		//这里会把图片添加到 活动缓存 中
		notifyComplete(result, dataSource);
		
		stage = Stage.ENCODE;
		try {
			if (deferredEncodeManager.hasResourceToEncode()) {
				//这里会把图片写入磁盘
				deferredEncodeManager.encode(diskCacheProvider, options);
			}
		} finally {
		  ...
		}
		...
	}
	
	private static class DeferredEncodeManager<Z> {
		private Key key;
		private ResourceEncoder<Z> encoder;
		private LockedResource<Z> 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<R> 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;
	}
}
5、LruBitmapPool

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<Bitmap>, ... {
	@Override
	public void recycle() {
		//把转换后的图片,保存到 LruBitmapPool 中
		bitmapPool.put(bitmap);
	}
}

public class BitmapDrawableResource extends DrawableResource<BitmapDrawable> ... {
	@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图片转换 Transformation

Glide 图片转换库 glide-transformations:https://github.com/wasabeef/glide-transformations

1、图片转换时机

Glide 图片的变换会在 图片解码后 执行

class DecodeJob<R> implements Runnable, Poolable, ... {
	
	<Z> Resource<Z> onResourceDecoded(DataSource dataSource, Resource<Z> decoded) {
		...
		Transformation<Z> appliedTransformation = null;
		Resource<Z> transformed = decoded;
		if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
			appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
			
			//执行图片转换
			transformed = appliedTransformation.transform(glideContext, decoded, width, height);
		}
		...
		return result;
	}
}
2、CenterCrop 图像转换的实现
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();
		}
	}
}
3、自定义图片转换

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;
	}
}

四、Glide加载回调

1、Glide 默认的回调 Target
public class RequestBuilder<TranscodeType> implements ... {
	
	public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
		...
		/* buildImageViewTarget()方法:根据 transcodeClass 的类型,构建一个 ViewTarget 对象
		 * 		BitmapImageViewTarget / DrawableImageViewTarget
		 */
		return into(glideContext.buildImageViewTarget(view, transcodeClass), null, requestOptions);
	}
	
	//这里会触发请求加载图片(活动缓存/内存缓存/磁盘缓存/网络)
	private <Y extends Target<TranscodeType>> Y into(@NonNull Y target, ...) {
		...
		requestManager.clear(target);
		target.setRequest(request);
		requestManager.track(target, request);
		return target;
	}
}

public class ImageViewTargetFactory {
	
	//据传入的class参数,构建一个 ViewTarget 对象
	public <Z> ViewTarget<ImageView, Z> buildTarget(ImageView view, Class<Z> clazz) {
		if (Bitmap.class.equals(clazz)) {
			return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
		} else if (Drawable.class.isAssignableFrom(clazz)) {
			return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
		} else {
			throw new IllegalArgumentException(
			  "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
		}
	}
}

public final class SingleRequest<R> implements Request, ResourceCallback, ... {
	private Target<R> target;
	...
	private void onResourceReady(Resource<R> 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<? super R> animation = animationFactory.build(dataSource, isFirstResource);
			
			/* 最终会触发 target.onResourceReady()
			 * BitmapImageViewTarget / DrawableImageViewTarget 中,
			 * 会调用view.setImageBitmap(resource) / view.setImageDrawable(resource)
			 * 把图片展示在控件上。
			 */
			target.onResourceReady(result, animation);
		}
		...
		notifyLoadSuccess();
	}
}
2、preload()方法,把图片预加载到【内存】中

GlideApp.with(config.getContext()).load(thumbnailUrl).preload();
通过 PreloadTarget 实现

public class RequestBuilder<TranscodeType> implements ... {
	
	public Target<TranscodeType> preload(int width, int height) {
		final PreloadTarget<TranscodeType> target = PreloadTarget.obtain(requestManager, width, height);
		return into(target);
	}

	/**
	* 
	*/
	public Target<TranscodeType> preload() {
		return preload(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
	}
	
	public Target<TranscodeType> preload(int width, int height) {
		final PreloadTarget<TranscodeType> target = PreloadTarget.obtain(requestManager, width, height);
		return into(target);
	}
}

public final class PreloadTarget<Z> extends SimpleTarget<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;
		}
	});
	...
	
	@Override
	public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
		HANDLER.obtainMessage(MESSAGE_CLEAR, this).sendToTarget();
	}

	@SuppressWarnings("WeakerAccess")
	@Synthetic void clear() {
		//图片下载完成后,移除 BaseTarget.request,释放资源
		requestManager.clear(this);
	}
}
3、downloadOnly()方法

仅仅下载图片,不加载到 内存

Glide.with(GlideManager.getApp())
	.downloadOnly()
	.load(url)
	.into(new SimpleTarget<File>() {
		@Override
		public void onResourceReady(File resource, Transition<? super File> transition) {
			//返回下载的文件 resource
		}

		@Override
		public void onLoadFailed(Drawable errorDrawable) {
			getter.onFail();
		}
	});

源码:

public class RequestBuilder<TranscodeType> implements ... {
	
	protected static final RequestOptions DOWNLOAD_ONLY_OPTIONS =
		new RequestOptions()
			.diskCacheStrategy(DiskCacheStrategy.DATA)
			.priority(Priority.LOW)
			.skipMemoryCache(true);//不加载到内存,仅仅是通过这个配置而已
	
	//只能在主线程调用
	public <Y extends Target<File>> Y downloadOnly(@NonNull Y target) {
		return getDownloadOnlyRequest().into(target);
	}
	
	/* 该方法内部调用 submit()的作用:
	 *    如果当前线程在子线程,会把下载的请求放到主线程去做,仅此而已。
	 *    在主线程也可以调用。
	 */
	public FutureTarget<File> downloadOnly(int width, int height) {
		return getDownloadOnlyRequest().submit(width, height);
	}

	protected RequestBuilder<File> getDownloadOnlyRequest() {
		return new RequestBuilder<>(File.class, this).apply(DOWNLOAD_ONLY_OPTIONS);
	}
	
	@NonNull
	public FutureTarget<TranscodeType> submit(int width, int height) {
		final RequestFutureTarget<TranscodeType> 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;
	}
}
4、listener() 方法
Glide.with(this)
      .load(url)
      .listener(new RequestListener<String, GlideDrawable>() {
          @Override
          public boolean onException(...) {
			/* 返回false就表示这个事件没有被处理,还会继续向下传递,
			 * 返回true就表示这个事件已经被处理掉了,从而不会再继续向下传递
			 */
              return false;
          }

          @Override
          public boolean onResourceReady(...) {
           /* 返回false就表示这个事件没有被处理,还会继续向下传递,
		    * 返回true就表示这个事件已经被处理掉了,从而不会再继续向下传递
		    */
			return false;
          }
      }).into(imageView);

源码:

public final classSingleRequest<R> implements Request, ... {
	
	private RequestListener<R> requestListener;
	
	private void onResourceReady(Resource<R> 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<? super R> animation =
				animationFactory.build(dataSource, isFirstResource);
			target.onResourceReady(result, animation);
		}
		...
		notifyLoadSuccess();
	}		
}

五、Glide加载进度监听实现

思路:拦截器,获取请求返回的数据 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<String, ProgressListener> 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<Drawable>() {
			@Override
			public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
				//加载结束后,移除进度监听器,避免内存泄露
				ProgressInterceptor.removeListener(url);
				return false;
			}

			@Override
			public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
				//加载结束后,移除进度监听器,避免内存泄露
				ProgressInterceptor.removeListener(url);
				return false;
			}
		}).into(img);
}	

六、Glide 优先级使用

public enum Priority {
	IMMEDIATE,	//立即执行(最高优先级)
	HIGH,		//高优先级
	NORMAL,		//普通优先级
	LOW,		//低优先级
}

使用:

RequestOptions options = new RequestOptions()
		.diskCacheStrategy(DiskCacheStrategy.DATA)
		.priority(Priority.LOW);

七、关于 Matrix 坐标矩阵

1、用于处理图像平面的 缩放、平移、旋转等操作

使用Matrix类的时候,图片的移动和旋转,只能在控件上面。
控件本身没有动,控件里面的内容动了

2、 set, pre, post方法
  • preXXX 表示 在队头插入一个方法
  • postXXX 表示 在队尾插入一个方法
  • setXXX 表示【把当前队列清空】,并且总是位于队列的最中间位置.
  • 当执行了一次 setXXX 后:
    preXXX 方法总是插入到 set前部的队列的最前面,
    postXXX 方法总是插入到 set后部的队列的最后面。

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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值