Glide 源碼淺析

分類:技術 時間:2016-10-25
Glide.with(this).load(file).into(mIv3);

從with開始

public static RequestManager with(Context context) {
 RequestManagerRetriever retriever = RequestManagerRetriever.get();
 return retriever.get(context);
 }

通過RequestManagerRetriever返回了一個RequestManager

RequestManagerRetriever

/**
 * A collection of static methods for creating new {@link com.bumptech.glide.RequestManager}s or retrieving existing
 * ones from activities and fragment.
 */

一個靜態方法集合,用于創建或者檢索,從Activitys和fragement集合中取回一個

  • 定義
    public class RequestManagerRetriever implements Handler.Callback
    

實現了Handler.Callback接口

public interface Callback {
 public boolean handleMessage(Message msg);
 }

Hanlder的回調方法

  • 類定義
    private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
    ....
    
    RequestManagerRetriever() {
     handler = new Handler(Looper.getMainLooper(), this /* Callback */);
     }
    
    
     public static RequestManagerRetriever get() {
     return INSTANCE;
     }
    

這個類是個單例的,在構造方法中,創建了一個在主線程的handler對象

通過get方法,得到這個類的實例對象

  • 通過get得到RequestManager

    public RequestManager get(Context context) {
     if (context == null) {
     //判斷上下文是否為空,
     throw new IllegalArgumentException("You cannot start a load on a null Context");
     } else if (Util.isOnMainThread()  !(context instanceof Application)) {
     //判斷是不是在主線程中,同時content是不是全局的,這里不是全局的,才進入
     //下面進行各種匹配
     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);
     }
    
  • get((FragmentActivity) context)之類的各種匹配

public RequestManager get(FragmentActivity activity) {
 if (Util.isOnBackgroundThread()) {
 return get(activity.getApplicationContext());
 } else {
 assertNotDestroyed(activity);
 FragmentManager fm = activity.getSupportFragmentManager();
 return supportFragmentGet(activity, fm);
 }
 }

判斷是不是后臺線程,是就回調上面的public RequestManager get(Context context)方法,再判斷

不是就先斷言Activity是不是銷毀了,是拋出異常,不是就創建一個FragmentManager,通過supportFragmentGet方法返回RequestManager

  • assertNotDestroyed

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     private static void assertNotDestroyed(Activity activity) {
     if (Build.VERSION.SDK_INT gt;= Build.VERSION_CODES.JELLY_BEAN_MR1  activity.isDestroyed()) {
     throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
     }
     }
    
  • 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對象這個就是一個fragment繼承fragment

public class SupportRequestManagerFragment extends Fragment {
  • SupportRequestManagerFragment
    SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {
     SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
     if (current == null) {
     current = pendingSupportRequestManagerFragments.get(fm);
     if (current == null) {
     current = new SupportRequestManagerFragment();
     pendingSupportRequestManagerFragments.put(fm, current);
     //就添加到集合中,開啟事務,
     fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
     //發送消息到Handler
     handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
     }
     }
     return current;
     }
    

首先獲取SupportRequestManagerFragment,在集合中取出

if (current == null) {
 current = pendingSupportRequestManagerFragments.get(fm);

再判斷是否為NULL,為空說明集合pendingSupportRequestManagerFragments中沒有

/** Pending adds for SupportRequestManagerFragments. */
 final Maplt;FragmentManager, SupportRequestManagerFragmentgt; pendingSupportRequestManagerFragments =
 new HashMaplt;FragmentManager, SupportRequestManagerFragmentgt;();
  • 通過構造方法new一個出來

    /**
    * 通過構造方法new一個出來
    */
    RequestManager supportFragmentGet(Context context, FragmentManager fm) {
    //先得到一個SupportRequestManagerFragment
     SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
     RequestManager requestManager = current.getRequestManager();
     if (requestManager == null) {
     requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
     current.setRequestManager(requestManager);
     }
     return requestManager;
    }
    
  • current.setRequestManager(requestManager);

private RequestManager requestManager;

public void setRequestManager(RequestManager requestManager) {
 this.requestManager = requestManager;
 }

public RequestManager getRequestManager() {
 return requestManager;
 }

先得到一個SupportRequestManagerFragment,通過SupportRequestManagerFragment的getRequestManager,得到requestManager

起到把請求和Activity生命周期同步的作用

  • getApplicationManager
    private RequestManager getApplicationManager(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.
     applicationManager = new RequestManager(context.getApplicationContext(),
     new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
     }
     }
     }
    
     return applicationManager;
    }
    
     /** The top application level RequestManager. */
     private volatile RequestManager applicationManager;
    

這里的applicationManager就是RequestManager

  • handleMessage

    @Override
     public boolean handleMessage(Message message) {
     boolean handled = true;
     Object removed = null;
     Object key = null;
     switch (message.what) {
     case ID_REMOVE_FRAGMENT_MANAGER:
     android.app.FragmentManager fm = (android.app.FragmentManager) message.obj;
     key = fm;
     removed = pendingRequestManagerFragments.remove(fm);
     break;
     case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER:
     FragmentManager supportFm = (FragmentManager) message.obj;
     key = supportFm;
     removed = pendingSupportRequestManagerFragments.remove(supportFm);
     break;
     default:
     handled = false;
     }
     if (handled  removed == null  Log.isLoggable(TAG, Log.WARN)) {
     Log.w(TAG, "Failed to remove expected request manager fragment, manager: "   key);
     }
     return handled;
     }
    

    handle中就是移除發送過來的對象FragmentManager

RequestManager

public class RequestManager implements LifecycleListener {

 public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
 this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
 }

 RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
 RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
 this.context = context.getApplicationContext();
 this.lifecycle = lifecycle;
 this.treeNode = treeNode;
 this.requestTracker = requestTracker;
 this.glide = Glide.get(context);
 this.optionsApplier = new OptionsApplier();

 ConnectivityMonitor connectivityMonitor = factory.build(context,
 new RequestManagerConnectivityListener(requestTracker));

 // 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 {
 lifecycle.addListener(this);
 }
 lifecycle.addListener(connectivityMonitor);
 }

這個類實現了,一個接口LifecycleListener有2個構造方法實際就一個,

/**
 * An interface for listener to {@link android.app.Fragment} and {@link android.app.Activity} lifecycle events.
 */
public interface LifecycleListener {

 /**
 * Callback for when {@link android.app.Fragment#onStart()}} or {@link android.app.Activity#onStart()} is called.
 */
 void onStart();

 /**
 * Callback for when {@link android.app.Fragment#onStop()}} or {@link android.app.Activity#onStop()}} is called.
 */
 void onStop();

 /**
 * Callback for when {@link android.app.Fragment#onDestroy()}} or {@link android.app.Activity#onDestroy()} is
 * called.
 */
 void onDestroy();
}

監聽生命周期的接口

  • private final Context context

    上下文沒什么說的

  • private final Lifecycle lifecycle一個接口用于添加生命監聽器LifecycleListener

    /**
     * An interface for listening to Activity/Fragment lifecycle events.
     */
    public interface Lifecycle {
     /**
     * Adds the given listener to the set of listeners managed by this Lifecycle implementation.
     */
     void addListener(LifecycleListener listener);
    }
    
  • private final RequestManagerTreeNode treeNode 同樣一個接口

/**
 * Provides access to the relatives of a RequestManager based on the current context. The context hierarchy
 * is provided by nesting in Activity and Fragments; the application context does not provide access to
 * any other RequestManagers hierarchically.
 */
public interface RequestManagerTreeNode {
 /**
 * Returns all descendant {@link RequestManager}s relative to the context of the current {@link RequestManager}.
 */
 Setlt;RequestManagergt; getDescendants();
}

用于獲取層次接口的Set集合,只能訪問本層次內的,不能訪問其它分支的

  • private final RequestTracker requestTracker;
/**
 * A class for tracking, canceling, and restarting in progress, completed, and failed requests.
 */
public class RequestTracker {

一個請求類,用于跟蹤取消,重啟進度,完成與失敗

public class RequestTracker {
 //一個集合,用于保存弱引用請求
 private final Setlt;Requestgt; requests = Collections.newSetFromMap(new WeakHashMaplt;Request, Booleangt;());
 // A set of requests that have not completed and are queued to be run again. We use this list to maintain hard
 // references to these requests to ensure that they are not garbage collected before they start running or
 // while they are paused. See #346.
 //一個集合,一組未完成的請求,并將要重新運行的請求 以確保它們不是在開始運行或運行之前收集的垃圾
 // 當他們被暫停的時候
 @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
 private final Listlt;Requestgt; pendingRequests = new ArrayListlt;Requestgt;();

//標識位是否暫停
 private boolean isPaused;

 /**
 * Starts tracking the given request.
 */
 public void runRequest(Request request) {
 requests.add(request);
 if (!isPaused) {
 request.begin();
 } else {
 pendingRequests.add(request);
 }
 }

 // Visible for testing.
 void addRequest(Request request) {
 requests.add(request);
 }

 /**
 * Stops tracking the given request.
 */
 public void removeRequest(Request request) {
 requests.remove(request);
 pendingRequests.remove(request);
 }

 /**
 * Returns {@code true} if requests are currently paused, and {@code false} otherwise.
 */
 public boolean isPaused() {
 return isPaused;
 }

 /**
 * Stops any in progress requests.
 */
 public void pauseRequests() {
 isPaused = true;
 for (Request request : Util.getSnapshot(requests)) {
 if (request.isRunning()) {
 request.pause();
 pendingRequests.add(request);
 }
 }
 }

 /**
 * Starts any not yet completed or failed requests.
 */
 public void resumeRequests() {
 isPaused = false;
 for (Request request : Util.getSnapshot(requests)) {
 if (!request.isComplete()  !request.isCancelled()  !request.isRunning()) {
 request.begin();
 }
 }
 pendingRequests.clear();
 }

 /**
 * Cancels all requests and clears their resources.
 */
 public void clearRequests() {
 for (Request request : Util.getSnapshot(requests)) {
 request.clear();
 }
 pendingRequests.clear();
 }

 /**
 * Restarts failed requests and cancels and restarts in progress requests.
 */
 public void restartRequests() {
 for (Request request : Util.getSnapshot(requests)) {
 if (!request.isComplete()  !request.isCancelled()) {
 // Ensure the request will be restarted in onResume.
 request.pause();
 if (!isPaused) {
 request.begin();
 } else {
 pendingRequests.add(request);
 }
 }
 }
 }
}
  • Request
public interface Request {

 /**
 * Starts an asynchronous load.
 */
 void begin();

 /**
 * Identical to {@link #clear()} except that the request may later be restarted.
 */
 void pause();

 /**
 * Prevents any bitmaps being loaded from previous requests, releases any resources held by this request,
 * displays the current placeholder if one was provided, and marks the request as having been cancelled.
 */
 void clear();

 /**
 * Returns true if this request is paused and may be restarted.
 */
 boolean isPaused();

 /**
 * Returns true if this request is running and has not completed or failed.
 */
 boolean isRunning();

 /**
 * Returns true if the request has completed successfully.
 */
 boolean isComplete();

 /**
 * Returns true if a non-placeholder resource is set. For Requests that load more than one resource, isResourceSet
 * may return true even if {@link #isComplete()}} returns false.
 */
 boolean isResourceSet();

 /**
 * Returns true if the request has been cancelled.
 */
 boolean isCancelled();

 /**
 * Returns true if the request has failed.
 */
 boolean isFailed();

 /**
 * Recycles the request object and releases its resources.
 */
 void recycle();
}

方法一目了然都是狀態需要子類實現

  • private final Glide glide;
this.glide = Glide.get(context);

這里才創建Glide對象

  • private final OptionsApplier optionsApplier;
class OptionsApplier {

 public lt;A, X extends GenericRequestBuilderlt;A, ?, ?, ?gt;gt; X apply(X builder) {
 if (options != null) {
 //option為DefaultOptions下面的內部接口
 options.apply(builder);
 }
 return builder;
 }
 }

一個內部類

  • private DefaultOptions options;
public interface DefaultOptions {
 /**
 * Allows the implementor to apply some options to the given request.
 *
 * @param requestBuilder The request builder being used to construct the load.
 * @param lt;Tgt; The type of the model.
 */
 lt;Tgt; void apply(GenericRequestBuilderlt;T, ?, ?, ?gt; requestBuilder);
 }

和optionsApplier配合使用

  • public static Glide get(Context context)
/**
 * Get the singleton.
 *
 * @return the singleton
 */
 public static Glide get(Context context) {
 if (glide == null) {
 synchronized (Glide.class) {
 if (glide == null) {
 Context applicationContext = context.getApplicationContext();
 Listlt;GlideModulegt; modules = new ManifestParser(applicationContext).parse();

 GlideBuilder builder = new GlideBuilder(applicationContext);
 for (GlideModule module : modules) {
 module.applyOptions(applicationContext, builder);
 }
 glide = builder.createGlide();
 for (GlideModule module : modules) {
 module.registerComponents(applicationContext, glide);
 }
 }
 }
 }

 return glide;
 }

獲取Glide對象,同步代碼塊,首先獲取上下文,

獲取一個GlideModule集合,

獲取GlideBuilder

對象其中的所有對象進行應用設置和注冊

初始化一些設置

public interface GlideModule {

 /**
 * Lazily apply options to a {@link com.bumptech.glide.GlideBuilder} immediately before the Glide singleton is
 * created.
 *
 * lt;pgt;
 * This method will be called once and only once per implementation.
 * lt;/pgt;
 *
 * @param context An Application {@link android.content.Context}.
 * @param builder The {@link com.bumptech.glide.GlideBuilder} that will be used to create Glide.
 */
 void applyOptions(Context context, GlideBuilder builder);

 /**
 * Lazily register components immediately after the Glide singleton is created but before any requests can be
 * started.
 *
 * lt;pgt;
 * This method will be called once and only once per implementation.
 * lt;/pgt;
 *
 * @param context An Application {@link android.content.Context}.
 * @param glide The newly created Glide singleton.
 */
 void registerComponents(Context context, Glide glide);
}
  • createGlide
Glide createGlide() {
 if (sourceService == null) {
 final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
 sourceService = new FifoPriorityThreadPoolExecutor(cores);
 }
 if (diskCacheService == null) {
 diskCacheService = new FifoPriorityThreadPoolExecutor(1);
 }

 MemorySizeCalculator calculator = new MemorySizeCalculator(context);
 if (bitmapPool == null) {
 if (Build.VERSION.SDK_INT gt;= Build.VERSION_CODES.HONEYCOMB) {
 int size = calculator.getBitmapPoolSize();
 bitmapPool = new LruBitmapPool(size);
 } else {
 bitmapPool = new BitmapPoolAdapter();
 }
 }

 if (memoryCache == null) {
 memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
 }

 if (diskCacheFactory == null) {
 diskCacheFactory = new InternalCacheDiskCacheFactory(context);
 }

 if (engine == null) {
 engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
 }

 if (decodeFormat == null) {
 decodeFormat = DecodeFormat.DEFAULT;
 }

 return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
 }

into方法

@Override
public Targetlt;GlideDrawablegt; into(ImageView view) {
 return super.into(view); //調父類的方法
}
  • 父類 GenericRequestBuilder-目前只有2種變換

    public Targetlt;TranscodeTypegt; into(ImageView view) {
     Util.assertMainThread();
     if (view == null) {
     throw new IllegalArgumentException("You must pass in a non null View");
     }
    
     if (!isTransformationSet  view.getScaleType() != null) {
     switch (view.getScaleType()) {
     case CENTER_CROP:
     //變換
     applyCenterCrop();
     break;
     case FIT_CENTER:
     case FIT_START:
     case FIT_END:
     //變換
     applyFitCenter();
     break;
     //$CASES-OMITTED$
     default:
     // Do nothing.
     }
     }
    
     return into(glide.buildImageViewTarget(view, transcodeClass));
     }
    
     /**
     * Returns a future that can be used to do a blocking get on a background thread.
     *
     * @param width The desired width in pixels, or {@link Target#SIZE_ORIGINAL}. This will be overridden by
     * {@link #override * (int, int)} if previously called.
     * @param height The desired height in pixels, or {@link Target#SIZE_ORIGINAL}. This will be overridden by
     * {@link #override * (int, int)}} if previously called).
     *
     * @see Glide#clear(com.bumptech.glide.request.FutureTarget)
     *
     * @return An {@link com.bumptech.glide.request.FutureTarget} that can be used to obtain the
     * resource in a blocking manner.
     */
     public FutureTargetlt;TranscodeTypegt; into(int width, int height) {
     final RequestFutureTargetlt;ModelType, TranscodeTypegt; target =
     new RequestFutureTargetlt;ModelType, TranscodeTypegt;(glide.getMainHandler(), width, height);
    
     // TODO: Currently all loads must be started on the main thread...
     glide.getMainHandler().post(new Runnable() {
     @Override
     public void run() {
     if (!target.isCancelled()) {
     into(target);
     }
     }
     });
    
     return target;
     }
    
    
    
     /**
     * Set the target the resource will be loaded into.
     *
     * @see Glide#clear(com.bumptech.glide.request.target.Target)
     *
     * @param target The target to load the resource into.
     * @return The given target.
     */
     public lt;Y extends Targetlt;TranscodeTypegt;gt; Y into(Y target) {
     Util.assertMainThread();
     if (target == null) {
     throw new IllegalArgumentException("You must pass in a non null Target");
     }
     if (!isModelSet) {
     throw new IllegalArgumentException("You must first set a model (try #load())");
     }
    
     Request previous = target.getRequest();
    
     if (previous != null) {
     previous.clear();
     requestTracker.removeRequest(previous);
     previous.recycle();
     }
    
     Request request = buildRequest(target);
     target.setRequest(request);
     lifecycle.addListener(target);
     requestTracker.runRequest(request);
    
     return target;
     }
    
  • RequestTracker

    /**
     * Starts tracking the given request.
     */
    public void runRequest(Request request) {
     requests.add(request);
     if (!isPaused) {
     //開始請求調父類的,佼
     request.begin();
     } else {
     //將請求添加到集合
     pendingRequests.add(request);
     }
    }
    

開始跟蹤請求

  • GenericRequest
@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));
 }
 }
  • onSizeReady
/**
 * A callback method that should never be invoked directly.
 */
 @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);

 ModelLoaderlt;A, Tgt; modelLoader = loadProvider.getModelLoader();
 final DataFetcherlt;Tgt; dataFetcher = modelLoader.getResourceFetcher(model, width, height);

 if (dataFetcher == null) {
 onException(new Exception("Failed to load model: \'"   model   "\'"));
 return;
 }
 ResourceTranscoderlt;Z, Rgt; transcoder = loadProvider.getTranscoder();
 if (Log.isLoggable(TAG, Log.VERBOSE)) {
 logV("finished setup for calling load in "   LogTime.getElapsedMillis(startTime));
 }
 loadedFromMemoryCache = true;
 //調用load方法
 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));
 }
 }

里面調用了engine.load方法

loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
 priority, isMemoryCacheable, diskCacheStrategy, this);
  • load方法
    public lt;T, Z, Rgt; LoadStatus load(Key signature, int width, int height, DataFetcherlt;Tgt; fetcher,
     DataLoadProviderlt;T, Zgt; loadProvider, Transformationlt;Zgt; transformation, ResourceTranscoderlt;Z, Rgt; 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());
    
     EngineResourcelt;?gt; 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;
     }
    
     EngineResourcelt;?gt; 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);
     DecodeJoblt;T, Z, Rgt; decodeJob = new DecodeJoblt;T, Z, Rgt;(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);
     }
    

加載資源方法

 private EngineResourcelt;?gt; loadFromActiveResources(Key key, boolean isMemoryCacheable) {
 if (!isMemoryCacheable) {
 return null;
 }

 EngineResourcelt;?gt; active = null;
 WeakReferencelt;EngineResourcelt;?gt;gt; activeRef = activeResources.get(key);
 if (activeRef != null) {
 active = activeRef.get();
 if (active != null) {
 active.acquire();
 } else {
 activeResources.remove(key);
 }
 }

 return active;
 }

 private EngineResourcelt;?gt; loadFromCache(Key key, boolean isMemoryCacheable) {
 if (!isMemoryCacheable) {
 return null;
 }

 EngineResourcelt;?gt; cached = getEngineResourceFromCache(key);
 if (cached != null) {
 cached.acquire();
 activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
 }
 return cached;
 }

 @SuppressWarnings("unchecked")
 private EngineResourcelt;?gt; getEngineResourceFromCache(Key key) {
 Resourcelt;?gt; cached = cache.remove(key);

 final EngineResource result;
 if (cached == null) {
 result = null;
 } else if (cached instanceof EngineResource) {
 // Save an object allocation if we've cached an EngineResource (the typical case).
 result = (EngineResource) cached;
 } else {
 result = new EngineResource(cached, true /*isCacheable*/);
 }
 return result;
 }

//釋放資源,索引  
 void acquire() {
 if (isRecycled) {
 throw new IllegalStateException("Cannot acquire a recycled resource");
 }
 if (!Looper.getMainLooper().equals(Looper.myLooper())) {
 throw new IllegalThreadStateException("Must call acquire on the main thread");
 }
   acquired;
 }

先從cache中尋找資源,如果找到則將其從cache中移除并放入activeResources中,否則從activeResources中尋找。cache是LruResourceCache對象,作為資源的LRU緩存;activeResources是以弱引用為值的Map,用于緩存使用中的資源。比一般內存緩存額外多一級緩存的意義在于,當內存不足時清理cache中的資源時,不會對使用中的Bitmap造成影響。

若再次尋找失敗,則創建EngineJob對象并調用其start方法。

  • EngineJob
public void start(EngineRunnable engineRunnable) {
 this.engineRunnable = engineRunnable;
 future = diskCacheService.submit(engineRunnable);
 }

 private final ExecutorService diskCacheService;
 private final ExecutorService sourceService;

diskCacheService是線程池

傳進來的參數engineRunnable,放在future = diskCacheService.submit(engineRunnable);執行

  • EngineRunnable
@Override
 public void run() {
 if (isCancelled) {
 return;
 }

 Exception exception = null;
 Resourcelt;?gt; 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 void onLoadComplete(Resource resource) {
 manager.onResourceReady(resource);
 }

 private void onLoadFailed(Exception e) {
 if (isDecodingFromCache()) {
 stage = Stage.SOURCE;
 manager.submitForSource(this);
 } else {
 manager.onException(e);
 }
 }

在EngineRunnable的run方法中進行編碼,根據緩存策略調用decodeFromCache或者decodeFromSource。

  • decode
private Resourcelt;?gt; decode() throws Exception {
 if (isDecodingFromCache()) {
 return decodeFromCache();
 } else {
 return decodeFromSource();
 }
 }
  • decodeFromCache

    private Resourcelt;?gt; decodeFromCache() throws Exception {
     Resourcelt;?gt; result = null;
     try {
     result = decodeJob.decodeResultFromCache();
     } catch (Exception e) {
     if (Log.isLoggable(TAG, Log.DEBUG)) {
     Log.d(TAG, "Exception decoding result from cache: "   e);
     }
     }
    
     if (result == null) {
     result = decodeJob.decodeSourceFromCache();
     }
     return result;
     }
    
  • decodeFromSource

private Resourcelt;?gt; decodeFromSource() throws Exception {
 return decodeJob.decodeFromSource();
 }
  • DecodeJob

    /**
     * Returns a transformed and transcoded resource decoded from source data in the disk cache, or null if no such
     * resource exists.
     *
     * @throws Exception
     */
     public Resourcelt;Zgt; decodeSourceFromCache() throws Exception {
     if (!diskCacheStrategy.cacheSource()) {
     return null;
     }
    
     long startTime = LogTime.getLogTime();
     Resourcelt;Tgt; decoded = loadFromCache(resultKey.getOriginalKey());
     if (Log.isLoggable(TAG, Log.VERBOSE)) {
     logWithTimeAndKey("Decoded source from cache", startTime);
     }
     return transformEncodeAndTranscode(decoded);
     }
    
     /**
     * Returns a transformed and transcoded resource decoded from source data, or null if no source data could be
     * obtained or no resource could be decoded.
     *
     * lt;pgt;
     * Depending on the {@link com.bumptech.glide.load.engine.DiskCacheStrategy} used, source data is either decoded
     * directly or first written to the disk cache and then decoded from the disk cache.
     * lt;/pgt;
     *
     * @throws Exception
     */
     public Resourcelt;Zgt; decodeFromSource() throws Exception {
     Resourcelt;Tgt; decoded = decodeSource();
     return transformEncodeAndTranscode(decoded);
     }
    
  • 構建fetcher.loadData()加載數據

     private Resourcelt;Tgt; decodeSource() throws Exception {
     Resourcelt;Tgt; decoded = null;
     try {
     long startTime = LogTime.getLogTime();
     final A data = http://www.tuicool.com/articles/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;
     }
    `
    

通過DataFetcher fetcher。

編碼前需要先通過DataFetcher訪問網絡獲得文件流。接口DataFetcher的實現類根據配置而不同,

之后根據需要將文件流寫入磁盤緩存,再對文件流進行編碼。

 public Resourcelt;Zgt; decodeFromSource() throws Exception {
 Resourcelt;Tgt; decoded = decodeSource();
 return transformEncodeAndTranscode(decoded);
 }


 //轉換

 private Resourcelt;Zgt; transformEncodeAndTranscode(Resourcelt;Tgt; decoded) {
 long startTime = LogTime.getLogTime();
 Resourcelt;Tgt; transformed = transform(decoded);
 if (Log.isLoggable(TAG, Log.VERBOSE)) {
 logWithTimeAndKey("Transformed resource from source", startTime);
 }

//寫入緩存
 writeTransformedToCache(transformed);

 startTime = LogTime.getLogTime();
 Resourcelt;Zgt; result = transcode(transformed);
 if (Log.isLoggable(TAG, Log.VERBOSE)) {
 logWithTimeAndKey("Transcoded transformed from source", startTime);
 }
 return result;
 }

//寫入緩存
 private void writeTransformedToCache(Resourcelt;Tgt; transformed) {
 if (transformed == null || !diskCacheStrategy.cacheResult()) {
 return;
 }
 long startTime = LogTime.getLogTime();
 SourceWriterlt;Resourcelt;Tgt;gt; writer = new SourceWriterlt;Resourcelt;Tgt;gt;(loadProvider.getEncoder(), transformed);
 diskCacheProvider.getDiskCache().put(resultKey, writer);
 if (Log.isLoggable(TAG, Log.VERBOSE)) {
 logWithTimeAndKey("Wrote transformed from source to cache", startTime);
 }
 }




 class SourceWriterlt;DataTypegt; implements DiskCache.Writer {

 private final Encoderlt;DataTypegt; encoder;
 private final DataType data;

 public SourceWriter(Encoderlt;DataTypegt; encoder, DataType data) {
 this.encoder = encoder;
 this.data = http://www.tuicool.com/articles/data;
 }

 @Override
 public boolean write(File file) {
 boolean success = false;
 OutputStream os = null;
 try {
 os = fileOpener.open(file);
 success = encoder.encode(data, os);
 } catch (FileNotFoundException e) {
 if (Log.isLoggable(TAG, Log.DEBUG)) {
 Log.d(TAG,"Failed to find file to write to disk cache", e);
 }
 } finally {
 if (os != null) {
 try {
 os.close();
 } catch (IOException e) {
 // Do nothing.
 }
 }
 }
 return success;
 }
 }
  • DiskLruCacheWrapper

    diskCacheProvider.getDiskCache().put(resultKey, writer);

    @Override
     public void put(Key key, Writer writer) {
     String safeKey = safeKeyGenerator.getSafeKey(key);
     writeLocker.acquire(key);
     try {
     DiskLruCache.Editor editor = getDiskCache().edit(safeKey);
     // Editor will be null if there are two concurrent puts. In the worst case we will just silently fail.
     if (editor != null) {
     try {
     File file = editor.getFile(0);
     if (writer.write(file)) {
     editor.commit();
     }
     } finally {
     editor.abortUnlessCommitted();
     }
     }
     } catch (IOException e) {
     if (Log.isLoggable(TAG, Log.WARN)) {
     Log.w(TAG, "Unable to put to disk cache", e);
     }
     } finally {
     writeLocker.release(key);
     }
     }
    
  • StreamBitmapDecoder – decoded = loadProvider.getSourceDecoder().decode(data, width, height);

StreamBitmapDecoder

@Override
 public Resourcelt;Bitmapgt; decode(InputStream source, int width, int height) {
 Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
 return BitmapResource.obtain(bitmap, bitmapPool);
 }
  • Downsampler
@Override
 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);
 }
 }

 private int getRoundedSampleSize(int degreesToRotate, int inWidth, int inHeight, int outWidth, int outHeight) {
 int targetHeight = outHeight == Target.SIZE_ORIGINAL ? inHeight : outHeight;
 int targetWidth = outWidth == Target.SIZE_ORIGINAL ? inWidth : outWidth;

 final int exactSampleSize;
 if (degreesToRotate == 90 || degreesToRotate == 270) {
 // If we're rotating the image  -90 degrees, we need to downsample accordingly so the image width is
 // decreased to near our target's height and the image height is decreased to near our target width.
 //noinspection SuspiciousNameCombination
 exactSampleSize = getSampleSize(inHeight, inWidth, targetWidth, targetHeight);
 } else {
 exactSampleSize = getSampleSize(inWidth, inHeight, targetWidth, targetHeight);
 }

 // BitmapFactory only accepts powers of 2, so it will round down to the nearest power of two that is less than
 // or equal to the sample size we provide. Because we need to estimate the final image width and height to
 // re-use Bitmaps, we mirror BitmapFactory's calculation here. For bug, see issue #224. For algorithm see
 // http://stackoverflow.com/a/17379704/800716.
 final int powerOfTwoSampleSize = exactSampleSize == 0 ? 0 : Integer.highestOneBit(exactSampleSize);

 // Although functionally equivalent to 0 for BitmapFactory, 1 is a safer default for our code than 0.
 return Math.max(1, powerOfTwoSampleSize);
 }

 private Bitmap downsampleWithSize(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
 BitmapFactory.Options options, BitmapPool pool, int inWidth, int inHeight, int sampleSize,
 DecodeFormat decodeFormat) {
 // Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
 Bitmap.Config config = getConfig(is, decodeFormat);
 options.inSampleSize = sampleSize;
 options.inPreferredConfig = config;
 if ((options.inSampleSize == 1 || Build.VERSION_CODES.KITKAT lt;= Build.VERSION.SDK_INT)  shouldUsePool(is)) {
 int targetWidth = (int) Math.ceil(inWidth / (double) sampleSize);
 int targetHeight = (int) Math.ceil(inHeight / (double) sampleSize);
 // BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.
 setInBitmap(options, pool.getDirty(targetWidth, targetHeight, config));
 }
 return decodeStream(is, bufferedStream, options);
 }

通過設置采樣率縮放圖片,降低內存占用,提高加載性能。

編碼之后通過transformEncodeAndTranscode方法進行轉換處理。–DecodeJob類

private Resourcelt;Tgt; transform(Resourcelt;Tgt; decoded) {
 if (decoded == null) {
 return null;
 }

 Resourcelt;Tgt; transformed = transformation.transform(decoded, width, height);
 if (!decoded.equals(transformed)) {
 decoded.recycle();
 }
 return transformed;
 }

transform方法調用了Transformation的transform方法。

Transformation是接口;

BitmapTransformation實現了該接口但留下了另一個抽象方法transform;

CenterCrop和FitCenter兩個類繼承了BitmapTransformation并實現了抽象方法transform。

  • BitmapTransformation
@Override
 public final Resourcelt;Bitmapgt; transform(Resourcelt;Bitmapgt; resource, int outWidth, int outHeight) {
 if (!Util.isValidDimensions(outWidth, outHeight)) {
 throw new IllegalArgumentException("Cannot apply transformation on width: "   outWidth   " or height: "
   outHeight   " less than or equal to zero and not Target.SIZE_ORIGINAL");
 }
 Bitmap toTransform = resource.get();
 int targetWidth = outWidth == Target.SIZE_ORIGINAL ? toTransform.getWidth() : outWidth;
 int targetHeight = outHeight == Target.SIZE_ORIGINAL ? toTransform.getHeight() : outHeight;
 Bitmap transformed = transform(bitmapPool, toTransform, targetWidth, targetHeight);

 final Resourcelt;Bitmapgt; result;
 if (toTransform.equals(transformed)) {
 result = resource;
 } else {
 result = BitmapResource.obtain(transformed, bitmapPool);
 }

 return result;
 }

 protected abstract Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight)
  • CenterCrop
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
 final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig() != null
 ? toTransform.getConfig() : Bitmap.Config.ARGB_8888);
 Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight);
 if (toReuse != null  toReuse != transformed  !pool.put(toReuse)) {
 toReuse.recycle();
 }
 return transformed;
 }
  • FitCenter
    @Override
     protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
     return TransformationUtils.fitCenter(toTransform, pool, outWidth, outHeight);
     }
    

由BitmapPool提供一個Bitmap作為下一步的Canvas載體。BitmapPool的實現類是LruBitmapPool,顧名思義是一個基于LRU方式的Bitmap緩存池,用于Bitmap的復用。

  • TransformationUtils
public static Bitmap centerCrop(Bitmap recycled, Bitmap toCrop, int width, int height) {
 if (toCrop == null) {
 return null;
 } else if (toCrop.getWidth() == width  toCrop.getHeight() == height) {
 return toCrop;
 }
 // From ImageView/Bitmap.createScaledBitmap.
 final float scale;
 float dx = 0, dy = 0;
 Matrix m = new Matrix();
 if (toCrop.getWidth() * height gt; width * toCrop.getHeight()) {
 scale = (float) height / (float) toCrop.getHeight();
 dx = (width - toCrop.getWidth() * scale) * 0.5f;
 } else {
 scale = (float) width / (float) toCrop.getWidth();
 dy = (height - toCrop.getHeight() * scale) * 0.5f;
 }

 m.setScale(scale, scale);
 m.postTranslate((int) (dx   0.5f), (int) (dy   0.5f));
 final Bitmap result;
 if (recycled != null) {
 result = recycled;
 } else {
 result = Bitmap.createBitmap(width, height, getSafeConfig(toCrop));
 }

 // We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given.
 TransformationUtils.setAlpha(toCrop, result);

 Canvas canvas = new Canvas(result);
 Paint paint = new Paint(PAINT_FLAGS);
 canvas.drawBitmap(toCrop, m, paint);
 return result;
 }

在TransformationUtils的centerCrop方法中,根據目標尺寸調整矩陣并繪制結果。

與上面的流程類似,FitCenter類的transform方法調用TransformationUtils的fitCenter方法調整Bitmap。

繼續看DecodeJob的transformEncodeAndTranscode方法。接下來調用writeTransformedToCache將轉換結果寫入磁盤緩存,再調用transcode方法進行轉碼。transcode方法中的transcoder的實際類型是GlideBitmapDrawableTranscoder。

  • GlideBitmapDrawableTranscoder
    @Override
     public Resourcelt;GlideBitmapDrawablegt; transcode(Resourcelt;Bitmapgt; toTranscode) {
     GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());
     return new GlideBitmapDrawableResource(drawable, bitmapPool);
     }
    

GlideBitmapDrawableTranscoder的transcode方法將Bitmap資源進行封裝。

到這里就結束了decodeFromSource的流程。

然后看decodeFromCache。

  • EngineRunnable-decodeFromCache
private Resourcelt;?gt; decodeFromCache() throws Exception {
 Resourcelt;?gt; result = null;
 try {
 result = decodeJob.decodeResultFromCache();
 } catch (Exception e) {
 if (Log.isLoggable(TAG, Log.DEBUG)) {
 Log.d(TAG, "Exception decoding result from cache: "   e);
 }
 }

 if (result == null) {
 result = decodeJob.decodeSourceFromCache();
 }
 return result;
 }

先調用DecodeJob的decodeResultFromCache方法獲取,獲取失敗則調用DecodeJob的decodeSourceFromCache方法。

  • decodeResultFromCache
    public Resourcelt;Zgt; decodeResultFromCache() throws Exception {
     if (!diskCacheStrategy.cacheResult()) {
     return null;
     }
    
     long startTime = LogTime.getLogTime();
     Resourcelt;Tgt; transformed = loadFromCache(resultKey);
     if (Log.isLoggable(TAG, Log.VERBOSE)) {
     logWithTimeAndKey("Decoded transformed from cache", startTime);
     }
     startTime = LogTime.getLogTime();
     Resourcelt;Zgt; result = transcode(transformed);
     if (Log.isLoggable(TAG, Log.VERBOSE)) {
     logWithTimeAndKey("Transcoded transformed from cache", startTime);
     }
     return result;
     }
    

先loadFromCache 再

  • loadFromCache

    private Resourcelt;Tgt; loadFromCache(Key key) throws IOException {
     File cacheFile = diskCacheProvider.getDiskCache().get(key);
     if (cacheFile == null) {
     return null;
     }
    
     Resourcelt;Tgt; result = null;
     try {
     result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
     } finally {
     if (result == null) {
     diskCacheProvider.getDiskCache().delete(key);
     }
     }
     return result;
     }
    
  • transcode

    private Resourcelt;Zgt; transcode(Resourcelt;Tgt; transformed) {
     if (transformed == null) {
     return null;
     }
     return transcoder.transcode(transformed);
    }
    
  • decodeSourceFromCache 就是上面 的decodeSource

    public Resourcelt;Zgt; decodeSourceFromCache() throws Exception {
     if (!diskCacheStrategy.cacheSource()) {
     return null;
     }
    
     long startTime = LogTime.getLogTime();
     Resourcelt;Tgt; decoded = loadFromCache(resultKey.getOriginalKey());
     if (Log.isLoggable(TAG, Log.VERBOSE)) {
     logWithTimeAndKey("Decoded source from cache", startTime);
     }
     return transformEncodeAndTranscode(decoded);
     }
    

decodeResultFromCache方法從磁盤緩存中獲取對應Bitmap并將其轉碼。

decodeSourceFromCache方法從磁盤緩存中獲取對應Bitmap并將其轉換(因為是原尺寸,需要調整大小)。

到這里結束了EngineRunnable的decode方法的流程。

  • EngineRunnable的decode是decodeFromCache和decodeFromSource二選一,decodeFromCache也獲取失敗的話就沒有然后了

    EngineRunnable

@Override
 public void run() {
 if (isCancelled) {
 return;
 }

 Exception exception = null;
 Resourcelt;?gt; 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);
 }
 }

失敗會走onLoadFailed,成功會走onLoadComplete

緩存中沒有結果的情況下會調用onLoadFailed方法,變更緩存策略重新放入線程池中執行,也就是從網絡獲取。這里的線程池是sourceService而不是上面的diskCacheService

得到了處理結果,接下來調用EngineRunnable的onLoadComplete方法將結果傳入

private void onLoadComplete(Resource resource) {
 manager.onResourceReady(resource);
 }

 private void onLoadFailed(Exception e) {
 if (isDecodingFromCache()) {
 stage = Stage.SOURCE;
 manager.submitForSource(this);
 } else {
 manager.onException(e);
 }
 }

onLoadComplete方法調用了EngineJob的onResourceReady方法。

  • EngineJob-onResourceReady
@Override
 public void onResourceReady(final Resourcelt;?gt; 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();
}

//異常處理
private void handleExceptionOnMainThread() {
 if (isCancelled) {
 return;
 } else if (cbs.isEmpty()) {
 throw new IllegalStateException("Received an exception without any callbacks to notify");
 }
 hasException = true;

 listener.onEngineJobComplete(key, null);

 for (ResourceCallback cb : cbs) {
 if (!isInIgnoredCallbacks(cb)) {
 cb.onException(exception);
 }
 }
}

onResourceReady方法中向Handler傳遞消息并由MainThreadCallback處理消息 ,也就切換到了主線程。

handleResultOnMainThread方法會調用GenericRequest的onResourceReady方法。

  • GenericRequest
    public void onResourceReady(Resourcelt;?gt; resource) {
     if (resource == null) {
     onException(new Exception("Expected to receive a Resourcelt;Rgt; 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);
     }
    
     private void onResourceReady(Resourcelt;?gt; 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)) {
     GlideAnimationlt;Rgt; 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);
     }
     }
    

onResourceReady方法調用GlideDrawableImageViewTarget的onResourceReady方法。

  • GlideDrawableImageViewTarget
 @Override
 public void onResourceReady(GlideDrawable resource, GlideAnimationlt;? super GlideDrawablegt; 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) lt;= SQUARE_RATIO_MARGIN
  Math.abs(drawableRatio - 1f) lt;= SQUARE_RATIO_MARGIN) {
 resource = new SquaringDrawable(resource, view.getWidth());
 }
 }
 super.onResourceReady(resource, animation);
 this.resource = resource;
 resource.setLoopCount(maxLoopCount);
 resource.start();
 }

 @Override
 protected void setResource(GlideDrawable resource) {
 view.setImageDrawable(resource);
 }

調用了父類的ImageViewTarget 中的方法


* ImageViewTarget

@Override

public void onResourceReady(Z resource, GlideAnimationlt;? super Zgt; glideAnimation) {

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

setResource(resource);

}

}

protected abstract void setResource(Z resource);

GlideDrawableImageViewTarget的onResourceReady方法調用其父類ImageViewTarget的onResourceReady方法,
而ImageViewTarget的onResourceReady方法中調用的抽象方法setResource在子類GlideDrawableImageViewTarget中實現,
該方法中調用了ImageView的setImageDrawable方法設置圖像。至此,整個加載流程就完成了。


![http://upload-images.jianshu.io/upload_images/2041538-689bc766123bdf43.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240](http://upload-images.jianshu.io/upload_images/2041538-689bc766123bdf43.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


### load方法 在RequestManager中

有很多的load方法 以url為例

@Deprecated

public DrawableTypeRequest

load(URL url) {

return (DrawableTypeRequest

) fromUrl().load(url);

}

@Deprecated
public DrawableTypeRequestlt;URLgt; fromUrl() {
    return loadGeneric(URL.class);
}
最后調的都是loadGeneric
private DrawableTypeRequest loadGeneric(Class

modelClass) {

ModelLoader

streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);

ModelLoader

fileDescriptorModelLoader =

Glide.buildFileDescriptorModelLoader(modelClass, context);

if (modelClass != null amp;amp; streamModelLoader == null amp;amp; fileDescriptorModelLoader == null) {

throw new IllegalArgumentException(“Unknown type “ modelClass “. You must provide a Model of a type for”

  quot; which there is a registered ModelLoader, if you are using a custom model, you must first callquot;
                  quot; Glide#register with a ModelLoaderFactory for your custom model classquot;);
    }

    return optionsApplier.apply(
            new DrawableTypeRequestlt;Tgt;(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
                    glide, requestTracker, lifecycle, optionsApplier));
}
* DrawableTypeRequest
DrawableTypeRequest(Class modelClass, ModelLoader

streamModelLoader,

ModelLoader

fileDescriptorModelLoader, Context context, Glide glide,

RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {

super(context, modelClass,

buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,

GlideDrawable.class, null),

glide, requestTracker, lifecycle);

this.streamModelLoader = streamModelLoader;

this.fileDescriptorModelLoader = fileDescriptorModelLoader;

this.optionsApplier = optionsApplier;

}

調用了super

* DrawableRequestBuilder

public class DrawableRequestBuilder extends GenericRequestBuilder implements BitmapOptions, DrawableOptions {

DrawableRequestBuilder(Context context, Classlt;ModelTypegt; modelClass,
        LoadProviderlt;ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawablegt; loadProvider, Glide glide,
        RequestTracker requestTracker, Lifecycle lifecycle) {
    super(context, modelClass, loadProvider, GlideDrawable.class, glide, requestTracker, lifecycle);
    // Default to animating.
    crossFade();
}
調用了crossFade設置淡入淡出效果
public final DrawableRequestBuilder

crossFade() {

super.animate(new DrawableCrossFadeFactory

());

return this;

}

*DrawableCrossFadeFactory 默認淡入淡出效果時間
public class DrawableCrossFadeFactory implements GlideAnimationFactory

{

private static final int DEFAULT_DURATION_MS = 300;

```


Tags: Bitmap 安卓開發

文章來源:https://ln0491.github.io/2016/10/24/Glide-源码浅析/


ads
ads

相關文章
ads

相關文章

ad