談到Glide,從英文字面意思有滑行、滑動的意思;而Android從開發的角度咱們知道它是一款圖片加載框架,這裏引用官方文檔的一句話「Glide是一個快速高效的Android圖片加載庫,注重於平滑的滾動」,從官方文檔介紹咱們瞭解到用Glide框架來加載圖片是快速而且高效的,接下來就來經過簡單使用Glide和源碼理解兩個方面看看Glide是不是快速和高效(文中代碼基於Glide 4.8版本)。html
1.使用前須要添加依賴java
implementation 'com.github.bumptech.glide:glide:4.8.0'
//使用Generated API須要引入
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
複製代碼
2.簡單加載網絡圖片到ImageView,能夠看到簡單一句代碼就能將網絡圖片加載到ImageView,也可使用Generated API方式android
//直接使用
Glide.with(Context).load(IMAGE_URL).into(mImageView)
//使用Generated API, 做用範圍Application 模塊內使用
//建立MyAppGlideModule類加上@GlideModule註解,make project 就能使用 GlideApp
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}
//Generated API加載圖片
GlideApp.with(Context).load(IMAGE_URL).into(mImageView);
複製代碼
3.當加載網絡圖片的時候,網絡請求是耗時操做,因此圖片不可能立刻就加載出來,網絡請求這段時間ImageView是空白的,因此咱們可使用一個佔位符顯示圖片來優化用戶體驗,佔位符有三種git
//添加佔位圖
RequestOptions requestOptions = new RequestOptions()
.placeholder(R.drawable.ic_cloud_download_black_24dp)
.error(R.drawable.ic_error_black_24dp)
.diskCacheStrategy(DiskCacheStrategy.NONE);//不使用緩存
Glide.with(Context).load(IMAGE_URL).apply(requestOptions).into(mImageView);
//Generated API 方式(和Glide3 同樣)
GlideApp.with(Context).load(IMAGE_URL)
.placeholder(R.drawable.ic_cloud_download_black_24dp)
.error(R.drawable.ic_error_black_24dp)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(mImageView);
// 後備回調符(Fallback) Generated API 方式纔有,在應用設置用戶頭像場景中,若是用戶不設置,也就是爲null的狀況,可使用後備回調符顯示默認頭像
private static final String NULL_URL=null;
GlideApp.with(Context).load(NULL_URL)
.fallback(R.drawable.ic_account_circle_black_24dp)
.into(mImageView);
複製代碼
4.指定加載圖片的大小(override)github
RequestOptions requestOptions = new RequestOptions().override(200,100);
Glide.with(Context).load(IMAGE_URL).apply(requestOptions).into(mImageView);
//Generated API 方式
GlideApp.with(Context).load(IMAGE_URL)
.override(200,100)
.into(mImageView);
複製代碼
5.縮略圖 (Thumbnail)緩存
//縮略圖Options
RequestOptions requestOptions = new RequestOptions()
.override(200,100)
.diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(Context)
.load(IMAGE_URL)
.thumbnail( Glide.with(this)
.load(IMAGE_URL)
.apply(requestOptions))
.into(mImageView);
//Generated API 方式
GlideApp.with(Context).
load(IMAGE_URL).
thumbnail( GlideApp.with(this)
.load(IMAGE_URL).override(200,100)
.diskCacheStrategy(DiskCacheStrategy.NONE)).into(mImageView);
複製代碼
6.圖像變化bash
Glide中內置了三種圖片的變化操做,分別是CenterCrop(圖片原圖的中心區域進行裁剪顯示),FitCenter(圖片原始長寬鋪滿)和CircleCrop(圓形裁剪)網絡
//顯示圓形裁剪到ImageView
RequestOptions requestOptions = new RequestOptions()
.circleCrop()
.diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(Context)
.load(IMAGE_URL)
.apply(requestOptions)
.into(mImageView);
//RequestOptions都內置了使用者三種變化的靜態方法
Glide.with(Context)
.load(IMAGE_URL)
.apply(RequestOptions.circleCropTransform())
.into(mImageView);
//Generated API 方式
GlideApp.with(Context).load(IMAGE_URL)
.circleCrop()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(mImageView);
複製代碼
若是想要更酷炫的變化,可使用第三方框架glide-transformations來幫助咱們實現,而且變化是能夠組合的app
//第三方框架glide-transformations引入
implementation 'jp.wasabeef:glide-transformations:4.0.0'
//使用glide-transformations框架 變換圖片顏色和加入模糊效果
RequestOptions requestOptions=new RequestOptions()
.placeholder(R.drawable.ic_cloud_download_black_24dp)
.transforms(new ColorFilterTransformation(Color.argb(80, 255, 0, 0)),new BlurTransformation(30))
.diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(Context).load(IMAGE_URL).
apply(requestOptions).
into(mImageView);
//Generated API 方式
GlideApp.with(Context).load(IMAGE_URL)
.transforms(new ColorFilterTransformation(Color.argb(80, 255, 0, 0)),new BlurTransformation(30))
.placeholder(R.drawable.ic_cloud_download_black_24dp)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(mImageView);
複製代碼
更多效果能夠查看官方例子框架
7.加載目標(Target)
Target是介於請求和請求者之間的中介者的角色,into方法的返回值就是target對象,以前咱們一直使用的 into(ImageView) ,它實際上是一個輔助方法,它接受一個 ImageView 參數併爲其請求的資源類型包裝了一個合適的 ImageViewTarget
//加載
Target<Drawable> target =
Glide.with(Context)
.load(url)
.into(new Target<Drawable>() {
...
});
//清除加載
Glide.with(Context).clear(target);
複製代碼
當咱們使用Notification顯示應用通知,若是想要自定義通知的界面,咱們須要用到RemoteView,若是要給RemoteView設置ImageView,根據提供的setImageViewBitmap方法,若是通知界面須要加載網絡圖片,則須要將網絡圖片轉換成bitmap,通常咱們能夠根據獲取圖片連接的流來轉換成bitmap,或者使用本文的主題使用Glide框架,這些都是耗時操做,感受操做起來很麻煩,而Glide框架很貼心的給我提供了NotificationTarget(繼承SimpleTarget),相對於咱們加載目標變成Notification
/**
* 新建 NotificationTarget 對象參數說明,與Glide3不一樣,Glide4的asBitmap()方法必須在load方法前面
* @param context 上下文對象
* @param viewId 須要加載ImageView的view的 id
* @param remoteViews RemoteView對象
* @param notification Notification對象
* @param notificationId Notification Id
*/
String iamgeUrl = "http://p1.music.126.net/fX0HfPMAHJ2L_UeJWsL7ig==/18853325881511874.jpg?param=130y130";
NotificationTarget notificationTarget = new NotificationTarget(mContext,R.id.notification_Image_play,mRemoteViews,mNotification,notifyId);
Glide.with(mContext.getApplicationContext())
.asBitmap()
.load(iamgeUrl)
.into( notificationTarget );
//Generated API 方式
GlideApp.with(mContext.getApplicationContext())
.asBitmap()
.load(iamgeUrl)
.into( notificationTarget );
複製代碼
8.回調監聽
Glide.with(this).load(IMAGE_URL).
listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
Toast.makeText(getApplicationContext(),"圖片加載失敗",Toast.LENGTH_SHORT).show();
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
Toast.makeText(getApplicationContext(),"圖片加載成功",Toast.LENGTH_SHORT).show();
return false;
}
}).into(mImageView);*/
//Generated API 方式
GlideApp.with(this).load(IMAGE_URL)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
Toast.makeText(getApplicationContext(),"圖片加載失敗",Toast.LENGTH_SHORT).show();
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
Toast.makeText(getApplicationContext(),"圖片加載成功",Toast.LENGTH_SHORT).show();
return false;
}
}).into(mImageView);
複製代碼
Glide還有其餘的一些使用方法,這裏就不繼續展開了,有興趣的能夠自行繼續研究。
Glide.with(Context).load(IMAGE_URL).into(mImageView);
複製代碼
/** Glide類的with()方法*/
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
@SuppressWarnings("deprecation")
@Deprecated
@NonNull
public static RequestManager with(@NonNull android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
@NonNull
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
複製代碼
/** Glide類的getRetriever()方法*/
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Context could be null for other reasons (ie the user passes in null), but in practice it will
// only occur due to errors with the Fragment lifecycle.
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
複製代碼
/** RequestManagerRetriever類的get()方法*/
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
@NonNull
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(fragment.getActivity(),
"You cannot start a load on a fragment before it is attached or after it is destroyed");
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
}
}
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull View view) {
if (Util.isOnBackgroundThread()) {
return get(view.getContext().getApplicationContext());
}
Preconditions.checkNotNull(view);
Preconditions.checkNotNull(view.getContext(),
"Unable to obtain a request manager for a view without a Context");
Activity activity = findActivity(view.getContext());
// The view might be somewhere else, like a service.
if (activity == null) {
return get(view.getContext().getApplicationContext());
}
// Support Fragments.
// Although the user might have non-support Fragments attached to FragmentActivity, searching
// for non-support Fragments is so expensive pre O and that should be rare enough that we
// prefer to just fall back to the Activity directly.
if (activity instanceof FragmentActivity) {
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get(activity);
}
// Standard Fragments.
android.app.Fragment fragment = findFragment(view, activity);
if (fragment == null) {
return get(activity);
}
return get(fragment);
}
複製代碼
/** RequestManagerRetriever類的getApplicationManager()方法*/
@NonNull
private RequestManager getApplicationManager(@NonNull Context context) {
// Either an application context or we're on a background thread. if (applicationManager == null) { synchronized (this) { if (applicationManager == null) { // Normally pause/resume is taken care of by the fragment we add to the fragment or // activity. However, in this case since the manager attached to the application will not // receive lifecycle events, we must force the manager to start resumed using // ApplicationLifecycle. // TODO(b/27524013): Factor out this Glide.get() call. Glide glide = Glide.get(context.getApplicationContext()); applicationManager = factory.build( glide, new ApplicationLifecycle(), new EmptyRequestManagerTreeNode(), context.getApplicationContext()); } } } return applicationManager; } 複製代碼
/** RequestManagerRetriever類的fragmentGet()方法*/
@SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"})
@Deprecated
@NonNull
private RequestManager fragmentGet(@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
/** RequestManagerRetriever類的getRequestManagerFragment()方法*/
@SuppressWarnings("deprecation")
@NonNull
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
複製代碼
/** RequestManager 類的as()方法*/
@NonNull
@CheckResult
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
/** RequestManager 類的as()方法*/
@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
/** RequestManager 類的部分load()方法*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
return asDrawable().load(bitmap);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
return asDrawable().load(drawable);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
//省略其餘參數類型 load() 方法
.......
複製代碼
/** RequestBuilder 類的load()方法*/
@NonNull
@CheckResult
@SuppressWarnings("unchecked")
@Override
public RequestBuilder<TranscodeType> load(@Nullable Object model) {
return loadGeneric(model);
}
/** RequestBuilder對象 類的loadGeneric()方法*/
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
複製代碼
/** RequestBuilder 類的apply方法*/
@NonNull
@CheckResult
public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
Preconditions.checkNotNull(requestOptions);
this.requestOptions = getMutableOptions().apply(requestOptions);
return this;
}
@SuppressWarnings("ReferenceEquality")
@NonNull
protected RequestOptions getMutableOptions() {
return defaultRequestOptions == this.requestOptions
? this.requestOptions.clone() : this.requestOptions;
}
複製代碼
/** RequestBuilder 類的into方法*/
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
RequestOptions requestOptions = this.requestOptions;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous // View's scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
複製代碼
/** GlideContext 類的 buildImageViewTarget方法*/
@NonNull
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public class ImageViewTargetFactory {
@NonNull
@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull 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 {
//省略代碼
.....
}
}
}
複製代碼
/** RequestBuilder 類的into方法返回的into方法*/
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
options = options.autoClone();
Request request = buildRequest(target, targetListener, options);
//省略部分代碼
......
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
複製代碼
private Request buildRequest(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
RequestOptions requestOptions) {
return buildRequestRecursive(
target,
targetListener,
/*parentCoordinator=*/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions);
}
private Request buildRequestRecursive(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
RequestOptions requestOptions) {
//省略部分代碼 error Request build
.....
Request mainRequest =
buildThumbnailRequestRecursive(
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions);
if (errorRequestCoordinator == null) {
return mainRequest;
}
//省略部分代碼 error Request build
.....
}
private Request buildThumbnailRequestRecursive(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
RequestOptions requestOptions) {
if (thumbnailBuilder != null) {
//省略部分代碼 縮略圖操做
.....
} else {
// Base case: no thumbnail.
return obtainRequest(
target,
targetListener,
requestOptions,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight);
}
}
private Request obtainRequest(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
RequestOptions requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight) {
return SingleRequest.obtain(
context,
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory());
}
複製代碼
public final class SingleRequest<R> implements Request,
SizeReadyCallback,
ResourceCallback,
FactoryPools.Poolable {
//省略部分代碼
......
/**SingleRequest類的 obtain方法*/
public static <R> SingleRequest<R> obtain(
Context context,
GlideContext glideContext,
Object model,
Class<R> transcodeClass,
RequestOptions requestOptions,
int overrideWidth,
int overrideHeight,
Priority priority,
Target<R> target,
RequestListener<R> targetListener,
@Nullable List<RequestListener<R>> requestListeners,
RequestCoordinator requestCoordinator,
Engine engine,
TransitionFactory<? super R> animationFactory) {
@SuppressWarnings("unchecked") SingleRequest<R> request =
(SingleRequest<R>) POOL.acquire();
if (request == null) {
request = new SingleRequest<>();
}
request.init(
context,
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
engine,
animationFactory);
return request;
}
//省略部分代碼
......
}
複製代碼
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target
複製代碼
/** RequestManager 類的track方法*/
void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
/** RequestTracker 類的runRequest方法*/
private final List<Request> pendingRequests = new ArrayList<>();
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
複製代碼
/** SingleRequest 類的begin方法*/
@Override
public void begin() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
private void onLoadFailed(GlideException e, int maxLogLevel) {
//省略部分代碼
.......
if (!anyListenerHandledUpdatingTarget) {
setErrorPlaceholder();
}
//省略部分代碼
.......
}
複製代碼
/** SingleRequest 類的setErrorPlaceholder方法*/
private void setErrorPlaceholder() {
if (!canNotifyStatusChanged()) {
return;
}
Drawable error = null;
if (model == null) {
error = getFallbackDrawable();
}
// Either the model isn't null, or there was no fallback drawable set. if (error == null) { error = getErrorDrawable(); } // The model isn't null, no fallback drawable was set or no error drawable was set.
if (error == null) {
error = getPlaceholderDrawable();
}
target.onLoadFailed(error);
}
private Drawable getErrorDrawable() {
if (errorDrawable == null) {
errorDrawable = requestOptions.getErrorPlaceholder();
if (errorDrawable == null && requestOptions.getErrorId() > 0) {
errorDrawable = loadDrawable(requestOptions.getErrorId());
}
}
return errorDrawable;
}
複製代碼
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>implements Transition.ViewAdapter {
//省略部分代碼
.......
public void setDrawable(Drawable drawable) {
view.setImageDrawable(drawable);
}
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
setResourceInternal(null);
setDrawable(placeholder);
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
setResourceInternal(null);
setDrawable(errorDrawable);
}
//省略部分代碼
.......
}
複製代碼
/** ViewTarget 類的etSize方法*/
void getSize(@NonNull SizeReadyCallback cb) {
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
return;
}
//省略部分代碼
......
}
複製代碼
/** SingleRequest 類的onSizeReady方法*/
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
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);
// This is a hack that's only useful for testing right now where loads complete synchronously // even though under any executor running on any thread but the main thread, the load would // have completed asynchronously. if (status != Status.RUNNING) { loadStatus = null; } if (IS_VERBOSE_LOGGABLE) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); } } 複製代碼
/** Engine 類的load方法*/
public <R> LoadStatus load(GlideContext glideContext, Object model,Key signature,int width,int height,Class<?> resourceClass,Class<R> transcodeClass, Priority priority,DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations,boolean isTransformationRequired,boolean isScaleOnlyOrNoTransform,Options options,boolean isMemoryCacheable,boolean useUnlimitedSourceExecutorPool,boolean useAnimationPool,boolean onlyRetrieveFromCache,ResourceCallback cb) {
Util.assertMainThread();
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
/** DecodeJob 類的繼承關係*/
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable
複製代碼
/** EngineJob 類的start方法*/
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
/** GlideExecutor 類的newSourceExecutor方法*/
public static GlideExecutor newSourceExecutor(
int threadCount, String name, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
return new GlideExecutor(
new ThreadPoolExecutor(
threadCount /* corePoolSize */,
threadCount /* maximumPoolSize */,
0 /* keepAliveTime */,
TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(),
new DefaultThreadFactory(name, uncaughtThrowableStrategy, false)));
}
複製代碼
/** DecodeJob 類的run方法*/
public void run() {
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (Throwable t) {
//省略部分代碼
.......
} finally {
if (localFetcher != null) {
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
/** DecodeJob 類的runWrapped方法*/
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
/** DecodeJob 類的getNextStage方法*/
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
/** DecodeJob 類的getNextGenerator方法*/
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
/** DecodeJob 類的runGenerators方法*/
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
//省略部分代碼
.......
}
複製代碼
/** SourceGenerator 類的startNext()方法*/
@Override
public boolean startNext() {
//省略部分代碼,跳過緩存部分判斷
........
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
/** DecodeHelper 類的getLoadData() 方法*/
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
LoadData<?> current =
modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
/** HttpGlideUrlLoader 類的buildLoadData 方法*/
@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
@NonNull Options options) {
// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
// spent parsing urls.
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
複製代碼
/**HttpUrlFetcher類的loadData方法 **/
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
}
}
}
/**HttpUrlFetcher類的loadDataWithRedirects方法 **/
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
Map<String, String> headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("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 HttpException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352. stream = urlConnection.getInputStream(); if (isCancelled) { return null; } final int statusCode = urlConnection.getResponseCode(); if (isHttpOk(statusCode)) { return getStreamForSuccessfulRequest(urlConnection); } else if (isHttpRedirect(statusCode)) { String redirectUrlString = urlConnection.getHeaderField("Location"); if (TextUtils.isEmpty(redirectUrlString)) { throw new HttpException("Received empty or null redirect url"); } URL redirectUrl = new URL(url, redirectUrlString); // Closing the stream specifically is required to avoid leaking ResponseBodys in addition // to disconnecting the url connection below. See #2352. cleanup(); return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers); } else if (statusCode == INVALID_STATUS_CODE) { throw new HttpException(statusCode); } else { throw new HttpException(urlConnection.getResponseMessage(), statusCode); } } 複製代碼
/**SourceGenerator類的onDataReady方法 **/
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should // reschedule to get back onto Glide's thread.
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
複製代碼
/**DecodeJob類的onDataFetcherReady方法 **/
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
複製代碼
/**DecodeJob類的decodeFromRetrievedData方法 **/
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Retrieved data", startFetchTime,
"data: " + currentData
+ ", cache key: " + currentSourceKey
+ ", fetcher: " + currentFetcher);
}
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
/**DecodeJob類的decodeFromData方法 **/
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
Resource<R> result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
fetcher.cleanup();
}
}
/**DecodeJob類的decodeFromFetcher方法 **/
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
複製代碼
/** DecodeHelper類的getLoadPath方法*/
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
/** Registry類的getLoadPath方法*/
@Nullable
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
//省略部分代碼
.......
List<DecodePath<Data, TResource, Transcode>> decodePaths =
getDecodePaths(dataClass, resourceClass, transcodeClass);
if (decodePaths.isEmpty()) {
result = null;
} else {
result =
new LoadPath<>(
dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
}
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}
/** Registry類的getDecodePaths方法*/
@NonNull
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
//省略部分代碼,去除干擾
List<ResourceDecoder<Data, TResource>> decoders =
decoderRegistry.getDecoders(dataClass, registeredResourceClass);
ResourceTranscoder<TResource, Transcode> transcoder =
transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
DecodePath<Data, TResource, Transcode> path =
new DecodePath<>(dataClass, registeredResourceClass, registeredTranscodeClass,
decoders, transcoder, throwableListPool);
decodePaths.add(path);
}
return decodePaths;
}
/** Registry類的getDecoders方法*/
public synchronized <T, R> List<ResourceDecoder<T, R>> getDecoders(@NonNull Class<T> dataClass,
@NonNull Class<R> resourceClass) {
List<ResourceDecoder<T, R>> result = new ArrayList<>();
for (String bucket : bucketPriorityList) {
List<Entry<?, ?>> entries = decoders.get(bucket);
if (entries == null) {
continue;
}
for (Entry<?, ?> entry : entries) {
if (entry.handles(dataClass, resourceClass)) {
result.add((ResourceDecoder<T, R>) entry.decoder);
}
}
}
// TODO: cache result list.
return result;
}
複製代碼
/**DecodePath類的decode方法**/
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
/**DecodePath類的decodeResource方法**/
@NonNull
private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,
int height, @NonNull Options options) throws GlideException {
List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
/**DecodePath類的decodeResourceWithList方法**/
@NonNull
private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
Resource<ResourceType> result = null;
//省略部分代碼
........
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options);
}
//省略部分代碼
........
return result;
}
複製代碼
/**StreamBitmapDecoder類的decode方法**/
@Override
public Resource<Bitmap> decode(@NonNull InputStream source, int width, int height,
@NonNull Options options)
throws IOException {
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
if (source instanceof RecyclableBufferedInputStream) {
bufferedStream = (RecyclableBufferedInputStream) source;
ownsBufferedStream = false;
} else {
bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
ownsBufferedStream = true;
}
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
try {
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
exceptionStream.release();
if (ownsBufferedStream) {
bufferedStream.release();
}
}
}
複製代碼
/**Downsampler類的decode方法**/
public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight,
Options options, DecodeCallbacks callbacks) throws IOException {
//省略部分代碼
try {
Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
downsampleStrategy, decodeFormat, isHardwareConfigAllowed, requestedWidth,
requestedHeight, fixBitmapToRequestedDimensions, callbacks);
return BitmapResource.obtain(result, bitmapPool);
} finally {
releaseOptions(bitmapFactoryOptions);
byteArrayPool.put(bytesForOptions);
}
}
/**Downsampler類的decodeFromWrappedStreams方法**/
private Bitmap decodeFromWrappedStreams(InputStream is,
BitmapFactory.Options options, DownsampleStrategy downsampleStrategy,
DecodeFormat decodeFormat, boolean isHardwareConfigAllowed, int requestedWidth,
int requestedHeight, boolean fixBitmapToRequestedDimensions,
DecodeCallbacks callbacks) throws IOException {
//省略部分代碼
.........
Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
callbacks.onDecodeComplete(bitmapPool, downsampled);
//省略部分代碼
.........
Bitmap rotated = null;
if (downsampled != null) {
//縮放效正處理
downsampled.setDensity(displayMetrics.densityDpi);
rotated = TransformationUtils.rotateImageExif(bitmapPool, downsampled, orientation);
if (!downsampled.equals(rotated)) {
bitmapPool.put(downsampled);
}
}
return rotated;
}
/**Downsampler類的decodeStream方法**/
private static Bitmap decodeStream(InputStream is, BitmapFactory.Options options,
DecodeCallbacks callbacks, BitmapPool bitmapPool) throws IOException {
if (options.inJustDecodeBounds) {
is.mark(MARK_POSITION);
} else {
callbacks.onObtainBounds();
}
int sourceWidth = options.outWidth;
int sourceHeight = options.outHeight;
String outMimeType = options.outMimeType;
final Bitmap result;
TransformationUtils.getBitmapDrawableLock().lock();
try {
result = BitmapFactory.decodeStream(is, null, options);
} catch (IllegalArgumentException e) {
//省略部分代碼
.........
return result;
}
複製代碼
/**DecodePath類的decode方法**/
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
/**BitmapDrawableTranscoder類的transcode方法**/
public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode,
@NonNull Options options) {
return LazyBitmapDrawableResource.obtain(resources, toTranscode);
}
/**LazyBitmapDrawableResource類的obtain方法**/
@Nullable
public static Resource<BitmapDrawable> obtain(
@NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
if (bitmapResource == null) {
return null;
}
return new LazyBitmapDrawableResource(resources, bitmapResource);
}
複製代碼
到此,Glide整個圖片解碼轉碼已近完成,接着咱們再回到DecodeJob對象的decodeFromRetrievedData方法
/**DecodeJob類的decodeFromRetrievedData方法**/
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
/**DecodeJob類的notifyEncodeAndRelease方法**/
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
//省略部分代碼
.....
Resource<R> result = resource;
//省略部分代碼
.....
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
//省略部分代碼
.....
}
/**DecodeJob類的notifyComplete方法**/
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
複製代碼
private static final Handler MAIN_THREAD_HANDLER =
new Handler(Looper.getMainLooper(), new MainThreadCallback());
/**EngineJob類的onResourceReady方法**/
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
/**EngineJob類的handleMessage方法**/
@Override
public boolean handleMessage(Message message) {
EngineJob<?> job = (EngineJob<?>) message.obj;
switch (message.what) {
case MSG_COMPLETE:
job.handleResultOnMainThread();
break;
case MSG_EXCEPTION:
job.handleExceptionOnMainThread();
break;
case MSG_CANCELLED:
job.handleCancelledOnMainThread();
break;
default:
throw new IllegalStateException("Unrecognized message: " + message.what);
}
return true;
}
/**EngineJob類的handleResultOnMainThread方法**/
@Synthetic
void handleResultOnMainThread() {
//省略部分代碼
......
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
//省略部分代碼
......
for (int i = 0, size = cbs.size(); i < size; i++) {
ResourceCallback cb = cbs.get(i);
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource, dataSource);
}
}
//省略部分代碼
......
}
/**EngineJob類的addCallback方法**/
private final List<ResourceCallback> cbs = new ArrayList<>(2);
void addCallback(ResourceCallback cb) {
Util.assertMainThread();
stateVerifier.throwIfRecycled();
if (hasResource) {
cb.onResourceReady(engineResource, dataSource);
} else if (hasLoadFailed) {
cb.onLoadFailed(exception);
} else {
cbs.add(cb);
}
}
複製代碼
/**SingleRequest類的onResourceReady回調方法**/
@SuppressWarnings("unchecked")
@Override
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
//省略部分代碼
.......
Object received = resource.get();
//省略部分代碼
.......
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
/**SingleRequest類的onResourceReady方法**/
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
//省略部分代碼
.......
status = Status.COMPLETE;
this.resource = resource;
//省略部分代碼
.......
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
}
//省略部分代碼
.......
}
複製代碼
經過以上源碼,這時候咱們已經能夠看到長征勝利的曙光了,SingleRequest對象的onResourceReady回調方法中調用了resource.get(),而這個resource就是前面咱們通過解碼、轉碼獲取的LazyBitmapDrawableResource對象,而後又調用了SingleRequest對象的onResourceReady私有方法,在該方法中又調用了target.onResourceReady方法,在咱們最開始進入into方法的時候咱們已經分析過建立的target對象就是DrawableImageViewTarget對象,它繼承了抽象類ImageViewTarget,因此咱們看看抽象類ImageViewTarget的onResourceReady方法
/** LazyBitmapDrawableResource類的get()方法**/
@NonNull
@Override
public BitmapDrawable get() {
return new BitmapDrawable(resources, bitmapResource.get());
}
/** ImageViewTarget類的onResourceReady方法**/
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
/** ImageViewTarget類的setResourceInternal方法**/
private void setResourceInternal(@Nullable Z resource) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
setResource(resource);
maybeUpdateAnimatable(resource);
}
protected abstract void setResource(@Nullable Z resource);
複製代碼
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
複製代碼
到此,咱們的萬里長征終於結束了,Glide的簡單加載圖片流程已經分析完了。
Glide.with(Context).load(IMAGE_URL).into(mImageView);
複製代碼
就是這樣一句簡單的代碼,它背後所走的邏輯卻讓人頭皮發麻,此時我只想說一句話「read the fuck source code」。前面咱們只是分析了Glide簡單的加載圖片流程,它的緩存使用,回調等功能原理還沒分析到,這隻能等到下篇文章了。文章中若是有錯誤,請你們給我提出來,你們一塊兒學習進步,若是以爲個人文章給予你幫助,也請給我一個喜歡和關注,同時也歡迎訪問個人我的博客。
對本文感興趣的朋友請繼續閱讀從源碼角度深刻理解Glide(下)
參考連接