android第三方库 glide,第三方开源库 Glide - 源码分析(补)

首先,在真正开始看源码之前,我们需要有个心里准备,决心是今天我们一定要搞懂它,不然充满好奇的进来,一脸蒙蔽的出去。带大家看了很多的第三方开源库和 Android 源码,又讲了 23 种设计模式基础,将这些结合起来,再去分析一些第三方库应该是 soEasy。只是希望我们自己能够去读懂,因为以后我们还会用到其他一些第三方框架。只有我们自己习得了看源码的能力才是最重要的。

其次,像 Glide 、Picasso、ImageLoader 这些访问网络的框架,其内部实现的原理应该大致差不多,线程池,连接网络,解析图片,缓存图片,显示图片。那像你这么说是不是开发中选择哪个都行?看心情选择第三方框架肯定是不行的。

最后,我们分析源码最好先从最基本的入手,调用简单方法一个方法一个方法走流程,最后再去抓细节。如果 UML 图你还没忘记还会画,那么可以画个 UML 时序图,便于我们走源代码流程。这篇文章的篇幅会很长请调整好呼吸:

1.Glide.with()

Glide.with() 的有很多重载方法,参数的类型都有 Context、Activity、FragmentActivity、android.support.v4.app.Fragment、android.app.Fragment 为什么要重载这么多方法,我们可以猜猜。其次,很多哥们在实际的开发过程中,可能都是传递的上下文 Context ,那么从现在开始最好不要这么传了。我选择其中一种分析一下:

public static RequestManager with(FragmentActivity activity) {

RequestManagerRetriever retriever = RequestManagerRetriever.get();

return retriever.get(activity);

}

public RequestManager get(FragmentActivity activity) {

if (Util.isOnBackgroundThread()) {

return get(activity.getApplicationContext());

} else {

assertNotDestroyed(activity);

FragmentManager fm = activity.getSupportFragmentManager();

return supportFragmentGet(activity, fm);

}

}

RequestManager supportFragmentGet(Context context, FragmentManager fm) {

SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);

RequestManager requestManager = current.getRequestManager();

if (requestManager == null) {

requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());

current.setRequestManager(requestManager);

}

return requestManager;

}

SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {

SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);

if (current == null) {

current = pendingSupportRequestManagerFragments.get(fm);

if (current == null) {

// 创建一个 Fragment 绑定到 Activity 上面

current = new SupportRequestManagerFragment();

pendingSupportRequestManagerFragments.put(fm, current);

fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();

handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();

}

}

return current;

}

上面是我挑的一些主要代码,主要的还是在 getSupportRequestManagerFragment 这个方法,这个方法干了啥?其实蛮好理解的,就是去创建一个 Fragment 然后把创建的 Fragment 绑定到 Activity 上面,搞这个干什么?其实是为了绑定监听 Activity 的生命周期,比如 RecyclerView 一个列表都在加载图片,这个时候我退出当前 Activity 应当停止访问网络加载图片,绑定生命周期就有这个好处。到底是怎么做监听的?看下源码:

RequestManager supportFragmentGet(Context context, FragmentManager fm) {

SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);

RequestManager requestManager = current.getRequestManager();

if (requestManager == null) {

requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());

current.setRequestManager(requestManager);

}

return requestManager;

}

RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,

RequestTracker requestTracker, ConnectivityMonitorFactory factory) {

// If we're the application level request manager, we may be created on a background thread. In that case we

// cannot risk synchronously pausing or resuming requests, so we hack around the issue by delaying adding

// ourselves as a lifecycle listener by posting to the main thread. This should be entirely safe.

if (Util.isOnBackgroundThread()) {

new Handler(Looper.getMainLooper()).post(new Runnable() {

@Override

public void run() {

lifecycle.addListener(RequestManager.this);

}

});

} else {

// 在这里注册为了 this

lifecycle.addListener(this);

}

lifecycle.addListener(connectivityMonitor);

}

LifecycleListener 是个接口有三个方法,onStart() , onStop(),onDestroy() 对应 RequestManager 中的实现分别是:

/**

* 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 void onStart() {

// onStart might not be called because this object may be created after the fragment/activity's onStart method.

resumeRequests();

}

/**

* Lifecycle callback that unregisters for connectivity events (if the android.permission.ACCESS_NETWORK_STATE

* permission is present) and pauses in progress loads.

*/

@Override

public void onStop() {

pauseRequests();

}

/**

* Lifecycle callback that cancels all in progress requests and clears and recycles resources for all completed

* requests.

*/

@Override

public void onDestroy() {

requestTracker.clearRequests();

}

所以总结一下Glide.with() 这方法返回的是一个 RequestManager 主要用来监听一些生命周期,以此来管理加载请求 Request 。拿 FragmentActivity 来说当 Activity 退出了会调用 onDestroy() -> requestTracker.clearRequests(); 会清理所有的图片加载请求,并且一个 FragmentActivity 只有一个 SupportRequestManagerFragment 和一个 RequestManager ,怎么看出来的?不是有那么多的 if else 判断代码,当然细节我们不用太关注,看下时序图:

223dc6205da2

Glide.with()

2.Glide.with().load()

public DrawableTypeRequest load(String string) {

return (DrawableTypeRequest) fromString().load(string);

}

public DrawableTypeRequest fromString() {

return loadGeneric(String.class);

}

private DrawableTypeRequest loadGeneric(Class modelClass) {

ModelLoader streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);

ModelLoader fileDescriptorModelLoader =

Glide.buildFileDescriptorModelLoader(modelClass, context);

if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {

throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"

+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"

+ " Glide#register with a ModelLoaderFactory for your custom model class");

}

return optionsApplier.apply(

new DrawableTypeRequest(modelClass, streamModelLoader, fileDescriptorModelLoader, context,

glide, requestTracker, lifecycle, optionsApplier));

}

Glide.buildStreamModelLoader 和 Glide.buildFileDescriptorModelLoader 这两个方法看不太懂先放一边,待会再回来找他,先记住他的变量名 streamModelLoader 和 fileDescriptorModelLoader,接下来创建了一个 DrawableTypeRequest 返回。这个方法比较简单,时序图:

223dc6205da2

Glide.with().load()

3.Glide.with().load().into()

public Target into(ImageView view) {

Util.assertMainThread();

if (view == null) {

throw new IllegalArgumentException("You must pass in a non null View");

}

return into(glide.buildImageViewTarget(view, transcodeClass));

}

public Target buildTarget(ImageView view, Class clazz) {

if (GlideDrawable.class.isAssignableFrom(clazz)) {

return (Target) new GlideDrawableImageViewTarget(view);

} else if (Bitmap.class.equals(clazz)) {

return (Target) new BitmapImageViewTarget(view);

} else if (Drawable.class.isAssignableFrom(clazz)) {

return (Target) new DrawableImageViewTarget(view);

} else {

throw new IllegalArgumentException("Unhandled class: " + clazz

+ ", try .as*(Class).transcode(ResourceTranscoder)");

}

}

public > Y into(Y target) {

Util.assertMainThread();

// 通过 target 构建一个 Request

Request request = buildRequest(target);

target.setRequest(request);

lifecycle.addListener(target);

// 看样子要执行

requestTracker.runRequest(request);

return target;

}

transcodeClass 是在构造函数里面赋值初始化的,也就是在第二步 load 的时候赋值的是 GlideDrawable.class 那么 buildTarget 方法返回的应该是 GlideDrawableImageViewTarget 对象,通过 Target

构建了一个 Request ,最后调用 requestTracker.runRequest(request); 接下来肯定要分析一下怎么构建 buildRequest 的:

private Request buildRequest(Target target) {

if (priority == null) {

priority = Priority.NORMAL;

}

return buildRequestRecursive(target, null);

}

// 这里省略了一个处理缩略图的方法,感兴趣可以自己研究一下

// obtainRequest

private Request obtainRequest(Target target, float sizeMultiplier, Priority priority,

RequestCoordinator requestCoordinator) {

// 构建了一个 GenericRequest 参数很多,但我肯定都知道是啥意思

return GenericRequest.obtain(

loadProvider,

model,

signature,

context,

priority,

target,

sizeMultiplier,

placeholderDrawable,

placeholderId,

errorPlaceholder,

errorId,

fallbackDrawable,

fallbackResource,

requestListener,

requestCoordinator,

glide.getEngine(),

transformation,

transcodeClass,

isCacheable,

animationFactory,

overrideWidth,

overrideHeight,

diskCacheStrategy);

}

buildRequest 构建的是一个 GenericRequest 实例,传递的参数很多但是我们肯定都认识,比如 placeholderId,errorId,transformation,diskCacheStrategy 等等,接下来看下真正的 runRequest() 方法:

/**

* Starts tracking the given request.

*/

public void runRequest(Request request) {

requests.add(request);

if (!isPaused) {

request.begin();

} else {

pendingRequests.add(request);

}

}

@Override

public void begin() {

startTime = LogTime.getLogTime();

if (model == null) {

// 异常报错显示错误图片

onException(null);

return;

}

status = Status.WAITING_FOR_SIZE;

if (Util.isValidDimensions(overrideWidth, overrideHeight)) {

onSizeReady(overrideWidth, overrideHeight);

} else {

target.getSize(this);

}

if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {

target.onLoadStarted(getPlaceholderDrawable());

}

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logV("finished run method in " + LogTime.getElapsedMillis(startTime));

}

}

@Override

public void onSizeReady(int width, int height) {

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));

}

if (status != Status.WAITING_FOR_SIZE) {

return;

}

status = Status.RUNNING;

width = Math.round(sizeMultiplier * width);

height = Math.round(sizeMultiplier * height);

ModelLoader modelLoader = loadProvider.getModelLoader();

final DataFetcher dataFetcher = modelLoader.getResourceFetcher(model, width, height);

if (dataFetcher == null) {

// 异常报错显示错误图片

onException(new Exception("Failed to load model: \'" + model + "\'"));

return;

}

ResourceTranscoder transcoder = loadProvider.getTranscoder();

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));

}

loadedFromMemoryCache = true;

loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,

priority, isMemoryCacheable, diskCacheStrategy, this);

loadedFromMemoryCache = resource != null;

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));

}

}

渐渐的开始复杂起来了,modelLoader 和 transcoder 都是通过 loadProvider 获取的,那么我们必须要了解一下 loadProvider 是怎么实例化的,他是在 obtainRequest 的时候传递初始化的,是在第二步 load 的时候在 DrawableTypeRequest 的构造方法中调用 buildProvider 构建的,是一个 FixedLoadProvider 。接下来看下 engine.load() 方法:

public LoadStatus load(Key signature, int width, int height, DataFetcher fetcher,

DataLoadProvider loadProvider, Transformation transformation, ResourceTranscoder transcoder,

Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {

Util.assertMainThread();

long startTime = LogTime.getLogTime();

final String id = fetcher.getId();

EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),

loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),

transcoder, loadProvider.getSourceEncoder());

EngineResource> cached = loadFromCache(key, isMemoryCacheable);

if (cached != null) {

cb.onResourceReady(cached);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Loaded resource from cache", startTime, key);

}

return null;

}

EngineResource> active = loadFromActiveResources(key, isMemoryCacheable);

if (active != null) {

cb.onResourceReady(active);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Loaded resource from active resources", startTime, key);

}

return null;

}

EngineJob current = jobs.get(key);

if (current != null) {

current.addCallback(cb);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Added to existing load", startTime, key);

}

return new LoadStatus(cb, current);

}

EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);

DecodeJob decodeJob = new DecodeJob(key, width, height, fetcher, loadProvider, transformation,

transcoder, diskCacheProvider, diskCacheStrategy, priority);

EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);

jobs.put(key, engineJob);

engineJob.addCallback(cb);

engineJob.start(runnable);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Started new load", startTime, key);

}

return new LoadStatus(cb, engineJob);

}

有一部分代码是处理缓存的,我们主要关注看下 DecodeJob 调用 start 方法,让线程池去执行 EngineRunnable 的 run 方法:

@Override

public void run() {

if (isCancelled) {

return;

}

Exception exception = null;

Resource> resource = null;

try {

resource = decode();

} catch (Exception e) {

if (Log.isLoggable(TAG, Log.VERBOSE)) {

Log.v(TAG, "Exception decoding", e);

}

exception = e;

}

if (isCancelled) {

// 如果被取消了,回收资源,不在往下执行

if (resource != null) {

resource.recycle();

}

return;

}

if (resource == null) {

// 失败

onLoadFailed(exception);

} else {

// 处理成功

onLoadComplete(resource);

}

}

private Resource> decode() throws Exception {

// 判断缓存

if (isDecodingFromCache()) {

return decodeFromCache();

} else {

return decodeFromSource();

}

}

private Resource> decodeFromSource() throws Exception {

return decodeJob.decodeFromSource();

}

最终调用的是 decodeJob.decodeFromSource() 方法,走了半天还没找到联网解析流的代码,应该在 decodeJob 里面吧?点进去看下:

public Resource decodeFromSource() throws Exception {

Resource decoded = decodeSource();

return transformEncodeAndTranscode(decoded);

}

private Resource transformEncodeAndTranscode(Resource decoded) {

long startTime = LogTime.getLogTime();

Resource transformed = transform(decoded);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Transformed resource from source", startTime);

}

writeTransformedToCache(transformed);

startTime = LogTime.getLogTime();

Resource result = transcode(transformed);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Transcoded transformed from source", startTime);

}

return result;

}

private Resource decodeSource() throws Exception {

Resource decoded = null;

try {

long startTime = LogTime.getLogTime();

final A data = fetcher.loadData(priority);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Fetched data", startTime);

}

if (isCancelled) {

return null;

}

decoded = decodeFromSourceData(data);

} finally {

fetcher.cleanup();

}

return decoded;

}

decodeFromSource()方法,其实它的工作分为两部,第一步是调用decodeSource()方法来获得一个Resource对象,第二步是调用transformEncodeAndTranscode()方法来处理这个Resource对象。在 decodeSource() 方法中调用了 fetcher.loadData(priority) 点击发现是个抽象方法,我们必须要找到实现类,要找到 fetcher 这个实现类那么先得找到 loadProvider 这个实现类,是在第二个 load 步骤中的 ImageVideoModelLoader 这个类,找到 getResourceFetcher() 返回的就是 fetcher 是 ImageVideoFetcher 类,找到 loadData() 方法如下:

@Override

public ImageVideoWrapper loadData(Priority priority) throws Exception {

InputStream is = null;

if (streamFetcher != null) {

try {

is = streamFetcher.loadData(priority);

} catch (Exception e) {

if (Log.isLoggable(TAG, Log.VERBOSE)) {

Log.v(TAG, "Exception fetching input stream, trying ParcelFileDescriptor", e);

}

if (fileDescriptorFetcher == null) {

throw e;

}

}

}

ParcelFileDescriptor fileDescriptor = null;

if (fileDescriptorFetcher != null) {

try {

fileDescriptor = fileDescriptorFetcher.loadData(priority);

} catch (Exception e) {

if (Log.isLoggable(TAG, Log.VERBOSE)) {

Log.v(TAG, "Exception fetching ParcelFileDescriptor", e);

}

if (is == null) {

throw e;

}

}

}

return new ImageVideoWrapper(is, fileDescriptor);

}

发现其方法内部又调用了 streamFetcher.loadData() 方法,streamFetcher 又是调用了 streamLoader.getResourceFetcher() 获取的,所以我们必须要找到 streamLoader , 它其实是第二步中通过 Glide.buildStreamModelLoader() 方法获取的,我们看下:

public synchronized ModelLoader buildModelLoader(Class modelClass, Class resourceClass) {

ModelLoader result = getCachedLoader(modelClass, resourceClass);

if (result != null) {

// We've already tried to create a model loader and can't with the currently registered set of factories,

// but we can't use null to demonstrate that failure because model loaders that haven't been requested

// yet will be null in the cache. To avoid this, we use a special signal model loader.

if (NULL_MODEL_LOADER.equals(result)) {

return null;

} else {

return result;

}

}

final ModelLoaderFactory factory = getFactory(modelClass, resourceClass);

if (factory != null) {

result = factory.build(context, this);

cacheModelLoader(modelClass, resourceClass, result);

} else {

// We can't generate a model loader for the given arguments with the currently registered set of factories.

cacheNullLoader(modelClass, resourceClass);

}

return result;

}

modelClass 是 String.class,resourceClass 是 InputStream.class 通过 getFactory 方法获取到 ModelLoaderFactory 对象,调用 factory.build() 方法构建了一个 ModelLoader 对象,是一个 StreamStringLoader 对象,过程有点曲折,但是琢磨琢磨还是能找到的。回到上面找 getResourceFetcher() 方法返回的是 HttpUrlFetcher 对象,接下来看他的 loadData() 方法:

@Override

public InputStream loadData(Priority priority) throws Exception {

return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());

}

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map headers)

throws IOException {

if (redirects >= MAXIMUM_REDIRECTS) {

throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");

} else {

// Comparing the URLs using .equals performs additional network I/O and is generally broken.

// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.

try {

if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {

throw new IOException("In re-direct loop");

}

} catch (URISyntaxException e) {

// Do nothing, this is best effort.

}

}

urlConnection = connectionFactory.build(url);

for (Map.Entry headerEntry : headers.entrySet()) {

urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());

}

urlConnection.setConnectTimeout(2500);

urlConnection.setReadTimeout(2500);

urlConnection.setUseCaches(false);

urlConnection.setDoInput(true);

// Connect explicitly to avoid errors in decoders if connection fails.

urlConnection.connect();

if (isCancelled) {

return null;

}

final int statusCode = urlConnection.getResponseCode();

if (statusCode / 100 == 2) {

return getStreamForSuccessfulRequest(urlConnection);

} else if (statusCode / 100 == 3) {

String redirectUrlString = urlConnection.getHeaderField("Location");

if (TextUtils.isEmpty(redirectUrlString)) {

throw new IOException("Received empty or null redirect url");

}

URL redirectUrl = new URL(url, redirectUrlString);

return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);

} else {

if (statusCode == -1) {

throw new IOException("Unable to retrieve response code from HttpUrlConnection.");

}

throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());

}

}

private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)

throws IOException {

if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {

int contentLength = urlConnection.getContentLength();

stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);

} else {

if (Log.isLoggable(TAG, Log.DEBUG)) {

Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());

}

stream = urlConnection.getInputStream();

}

return stream;

}

上面这个代码一般都能读懂了,就是通过 urlConnection 联网请求数据,这里返回的是一个 InputStream 并没有做任何的一些其他处理,所以我们才刚刚开始,我其实还是比较关心他怎么处理 InputStream 的,因为开发中经常用到,刚好可以借鉴一下。接着回到上面去看下 decodeFromSourceData 这个方法:

private Resource decodeFromSourceData(A data) throws IOException {

final Resource decoded;

if (diskCacheStrategy.cacheSource()) {

decoded = cacheAndDecodeSourceData(data);

} else {

long startTime = LogTime.getLogTime();

decoded = loadProvider.getSourceDecoder().decode(data, width, height);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Decoded from source", startTime);

}

}

return decoded;

}

调用了 loadProvider.getSourceDecoder().decode() 方法来进行解码。loadProvider 就是刚才在 onSizeReady() 方法中得到的FixedLoadProvider,而 getSourceDecoder() 得到的则是一个 GifBitmapWrapperResourceDecoder 对象,也就是要调用这个对象的 decode() 方法来对图片进行解码。那么我们来看下 GifBitmapWrapperResourceDecoder 的代码:

// @see ResourceDecoder.decode

@Override

public Resource decode(ImageVideoWrapper source, int width, int height) throws IOException {

ByteArrayPool pool = ByteArrayPool.get();

byte[] tempBytes = pool.getBytes();

GifBitmapWrapper wrapper = null;

try {

wrapper = decode(source, width, height, tempBytes);

} finally {

pool.releaseBytes(tempBytes);

}

return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;

}

private GifBitmapWrapper decode(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException {

final GifBitmapWrapper result;

if (source.getStream() != null) {

result = decodeStream(source, width, height, bytes);

} else {

result = decodeBitmapWrapper(source, width, height);

}

return result;

}

private GifBitmapWrapper decodeStream(ImageVideoWrapper source, int width, int height, byte[] bytes)

throws IOException {

InputStream bis = streamFactory.build(source.getStream(), bytes);

bis.mark(MARK_LIMIT_BYTES);

ImageHeaderParser.ImageType type = parser.parse(bis);

bis.reset();

GifBitmapWrapper result = null;

if (type == ImageHeaderParser.ImageType.GIF) {

result = decodeGifWrapper(bis, width, height);

}

// Decoding the gif may fail even if the type matches.

if (result == null) {

// We can only reset the buffered InputStream, so to start from the beginning of the stream, we need to

// pass in a new source containing the buffered stream rather than the original stream.

ImageVideoWrapper forBitmapDecoder = new ImageVideoWrapper(bis, source.getFileDescriptor());

result = decodeBitmapWrapper(forBitmapDecoder, width, height);

}

return result;

}

private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {

GifBitmapWrapper result = null;

Resource bitmapResource = bitmapDecoder.decode(toDecode, width, height);

if (bitmapResource != null) {

result = new GifBitmapWrapper(bitmapResource, null);

}

return result;

}

decodeStream 会判断是不是 Gif , 如果不是 Gif 会调用 decodeBitmapWrapper 方法,这个时候会调用 bitmapDecoder.decode() 方法,这个方法应该要去处理图片了?赶紧找到实现类 StreamBitmapDecoder 看下:

@Override

public Resource decode(InputStream source, int width, int height) {

Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);

return BitmapResource.obtain(bitmap, bitmapPool);

}

public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {

final ByteArrayPool byteArrayPool = ByteArrayPool.get();

final byte[] bytesForOptions = byteArrayPool.getBytes();

final byte[] bytesForStream = byteArrayPool.getBytes();

final BitmapFactory.Options options = getDefaultOptions();

// Use to fix the mark limit to avoid allocating buffers that fit entire images.

RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(

is, bytesForStream);

// Use to retrieve exceptions thrown while reading.

// TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a way to determine

// if a Bitmap is partially decoded, consider removing.

ExceptionCatchingInputStream exceptionStream =

ExceptionCatchingInputStream.obtain(bufferedStream);

// Use to read data.

// Ensures that we can always reset after reading an image header so that we can still attempt to decode the

// full image even when the header decode fails and/or overflows our read buffer. See #283.

MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);

try {

exceptionStream.mark(MARK_POSITION);

int orientation = 0;

try {

orientation = new ImageHeaderParser(exceptionStream).getOrientation();

} catch (IOException e) {

if (Log.isLoggable(TAG, Log.WARN)) {

Log.w(TAG, "Cannot determine the image orientation from header", e);

}

} finally {

try {

exceptionStream.reset();

} catch (IOException e) {

if (Log.isLoggable(TAG, Log.WARN)) {

Log.w(TAG, "Cannot reset the input stream", e);

}

}

}

options.inTempStorage = bytesForOptions;

final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);

final int inWidth = inDimens[0];

final int inHeight = inDimens[1];

final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);

final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);

final Bitmap downsampled =

downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize,

decodeFormat);

// BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch

// and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps,

// we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here.

final Exception streamException = exceptionStream.getException();

if (streamException != null) {

throw new RuntimeException(streamException);

}

Bitmap rotated = null;

if (downsampled != null) {

rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);

if (!downsampled.equals(rotated) && !pool.put(downsampled)) {

downsampled.recycle();

}

}

return rotated;

} finally {

byteArrayPool.releaseBytes(bytesForOptions);

byteArrayPool.releaseBytes(bytesForStream);

exceptionStream.release();

releaseOptions(options);

}

}

到这里我们就真正开始解析 Bitmap 了,就是从 InputStream 中去获取 Bitmap 对象,这些代码还是比较容易看懂的,Downsampler.decode() 返回的是一个 Bitmap 对象,StreamBitmapDecoder.decode() 对 Bitmap 又进行了一层包裹,返回的是 BitmapResource 。接下来我们回到 GifBitmapWrapperResourceDecoder.decode 中返回的是 GifBitmapWrapperResource , 接着往回走就回到了 DecodeJob.transformEncodeAndTranscode() 中:

private Resource transformEncodeAndTranscode(Resource decoded) {

long startTime = LogTime.getLogTime();

Resource transformed = transform(decoded);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Transformed resource from source", startTime);

}

writeTransformedToCache(transformed);

startTime = LogTime.getLogTime();

Resource result = transcode(transformed);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Transcoded transformed from source", startTime);

}

return result;

}

private Resource transform(Resource decoded) {

if (decoded == null) {

return null;

}

Resource transformed = transformation.transform(decoded, width, height);

if (!decoded.equals(transformed)) {

decoded.recycle();

}

return transformed;

}

private Resource transcode(Resource transformed) {

if (transformed == null) {

return null;

}

return transcoder.transcode(transformed);

}

上面主要分为三步,transform(),writeTransformedToCache() 写入缓存,transcode() 这里要注意了是把 泛型 T 转成了 Z 。transcode() 方法中又是调用了 transcoder 的 transcode() 方法,发现又找不到代码了,所以我们有得回去找 transcoder 实现类,这也是说我们为什么最好要会画 UML 图,因为代码架构复杂的情况下,我们在里面游几天都上不来。还是回去找找,是在第二步 load 的时候创建的 glide.buildTranscoder(resourceClass, transcodedClass) 是 GifBitmapWrapperDrawableTranscoder :

@SuppressWarnings("unchecked")

@Override

public Resource transcode(Resource toTranscode) {

GifBitmapWrapper gifBitmap = toTranscode.get();

Resource bitmapResource = gifBitmap.getBitmapResource();

final Resource extends GlideDrawable> result;

if (bitmapResource != null) {

result = bitmapDrawableResourceTranscoder.transcode(bitmapResource);

} else {

result = gifBitmap.getGifResource();

}

// This is unchecked but always safe, anything that extends a Drawable can be safely cast to a Drawable.

return (Resource) result;

}

@Override

public Resource transcode(Resource toTranscode) {

GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());

return new GlideBitmapDrawableResource(drawable, bitmapPool);

}

这里主要是把 GifBitmapWrapper 转成了 GlideDrawable ,为什么这么弄肯定是为了后面方便显示,因为 Gif 资源返回的已经是 GifDrawable 了并不需要转换,而图片资源返回的是 Bitmap ,做一次转换之后统一转成 GlideBitmapDrawable 这样方便后面做显示,那么显示我们要回到 EngineRunnable 中的 run 方法了:

@Override

public void run() {

if (isCancelled) {

return;

}

Exception exception = null;

Resource> resource = null;

try {

resource = decode();

} catch (Exception e) {

if (Log.isLoggable(TAG, Log.VERBOSE)) {

Log.v(TAG, "Exception decoding", e);

}

exception = e;

}

if (isCancelled) {

if (resource != null) {

resource.recycle();

}

return;

}

if (resource == null) {

onLoadFailed(exception);

} else {

onLoadComplete(resource);

}

}

现在我们来总结一下 decode() 方法都干了啥,首先是 DecodeJob.decodeFromSource() -> fetcher.loadData() 去联网获取 InputStream -> loadProvider.getSourceDecoder().decode() -> bitmapDecoder.decode() 解析 InputStream 压缩旋转等等返回 Bitmap -> DecodeJob.transformEncodeAndTranscode() 去转换保证返回数据一致 GlideDrawable 。接下来只需要看下 onLoadComplete 应该就要完了:

private void onLoadComplete(Resource resource) {

manager.onResourceReady(resource);

}

@Override

public void onResourceReady(final Resource> resource) {

this.resource = resource;

MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();

}

private static class MainThreadCallback implements Handler.Callback {

@Override

public boolean handleMessage(Message message) {

if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {

EngineJob job = (EngineJob) message.obj;

if (MSG_COMPLETE == message.what) {

job.handleResultOnMainThread();

} else {

job.handleExceptionOnMainThread();

}

return true;

}

return false;

}

}

private void handleResultOnMainThread() {

if (isCancelled) {

resource.recycle();

return;

} else if (cbs.isEmpty()) {

throw new IllegalStateException("Received a resource without any callbacks to notify");

}

engineResource = engineResourceFactory.build(resource, isCacheable);

hasResource = true;

// Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it

// synchronously released by one of the callbacks.

engineResource.acquire();

listener.onEngineJobComplete(key, engineResource);

for (ResourceCallback cb : cbs) {

if (!isInIgnoredCallbacks(cb)) {

engineResource.acquire();

cb.onResourceReady(engineResource);

}

}

// Our request is complete, so we can release the resource.

engineResource.release();

}

这里主要是切换到主线程,然后循环 cbs 调用 cb 的 onResourceReady() 方法,那么 cb 又是啥?其实就是 GenericRequest.this,看下 onResourceReady():

@Override

public void onResourceReady(Resource> resource) {

if (resource == null) {

onException(new Exception("Expected to receive a Resource with an object of " + transcodeClass

+ " inside, but instead got null."));

return;

}

Object received = resource.get();

if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {

releaseResource(resource);

onException(new Exception("Expected to receive an object of " + transcodeClass

+ " but instead got " + (received != null ? received.getClass() : "") + "{" + received + "}"

+ " inside Resource{" + resource + "}."

+ (received != null ? "" : " "

+ "To indicate failure return a null Resource object, "

+ "rather than a Resource object containing null data.")

));

return;

}

if (!canSetResource()) {

releaseResource(resource);

// We can't set the status to complete before asking canSetResource().

status = Status.COMPLETE;

return;

}

onResourceReady(resource, (R) received);

}

/**

* Internal {@link #onResourceReady(Resource)} where arguments are known to be safe.

*

* @param resource original {@link Resource}, never null

* @param result object returned by {@link Resource#get()}, checked for type and never null

*/

private void onResourceReady(Resource> resource, R result) {

// We must call isFirstReadyResource before setting status.

boolean isFirstResource = isFirstReadyResource();

status = Status.COMPLETE;

this.resource = resource;

if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,

isFirstResource)) {

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

}

}

最终调用了 target 的 onResourceReady() 方法,那么 target 又是谁呢?不知道我们是否还记得这代码into(glide.buildImageViewTarget(view, transcodeClass)) 在之前就分析过的 GlideDrawableImageViewTarget :

@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

public void onResourceReady(Z resource, GlideAnimation super Z> glideAnimation) {

if (glideAnimation == null || !glideAnimation.animate(resource, this)) {

setResource(resource);

}

}

@Override

protected void setResource(GlideDrawable resource) {

view.setImageDrawable(resource);

}

223dc6205da2

Glide.with().load().into()

走下流程其实还是非常简单的,就是需要大家多花些时间多点耐心,这第一步才刚刚开始,如果第一步迈不过去,后面我们就无法分析细节,比如缓存的处理,架构的模式,图片和Gif以及视频的解析处理,等等。源码也不能白看,很多思想是要用到实际项目中的,后面文章我将陆陆续续去分析。还是那句话 Java基础和设计模式基础打牢,自己习得了看源码的能力才是最重要的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值