<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Glide源碼導讀

      最近比較無聊,為了找點事干,就花了兩天時間把Glide的源碼大概看了一下。剛開始看Glide的源碼頭腦還是比較亂的,因為作者引入了幾個概念,又大量用了泛型,如果不了解這些概念讀起代碼來就比較痛苦,我也沒有詳細看各種實現細節的東西,只是了解了下這個框架的大概樣子,在這篇文章里,我會介紹下Glide中的一些關鍵概念,并走一遍圖片加載流程,如果你要閱讀Glide源碼的話,應該多少會有點幫助。

      基本概念

      首先是三個最基本的概念:Model, DataResource

      想一下,我們加載圖片需要什么?一般是一個url,但url并不是所有情況,還有資源ID,文件等等,甚至可以是Feed流中的一條Feed,雖然一般我們會從Feed中取出圖片的url來轉換為從url中加載的情況,Glide把這些抽像為了一個概念,就是Model,所以Model就是數據地址的最初來源。

      Model并不能直接解析為圖片,比如一個url,是要轉換為網絡流的InputStream才能被解析為圖片的,Model需要進行一次轉換才能做為數據解析的數據源,這些轉換后的東西就叫做Data,Glide并沒有一個Data類,但有很多和它相關的概念,如dataClase,DataFetcher等。

      那么Resource呢,其實它就是一個包裝類,一個wrapper,它wrap一個對象,使這個對象可以通過對象池進行緩存與重用。

      這三個基本概念介紹完了,接下來看一下Glide基本框架。

      做為一個圖片加載框架,肯定會包含緩存部分。

      可以從網上很容易的了解到,Glide的磁盤緩存可以緩存原始數據,也可以緩存處理過的數據。什么意思呢,就是你有一張1000x1000的圖片,但你是在列表中展示的,比如是200x200,那么緩存時可以直接將整個網絡流緩存下來,即1000x1000的圖片,要展示的時候再縮放,但這就降低了展示效率,所以Glide也可以把處理過的200x200的圖片緩存起來,增加了緩存大小,但優化了展示速度。

      至于怎么把數據緩存到磁盤,就引入了一個叫Encoder的概念,Encoder是用來持久化數據的。

      但看源碼時你會發現,Glide中有一個類叫Registry,可以注冊多個Encoder,但你會發現它還可以注冊ResourceEncoder。這兩個Encoder很容易引起混淆,而其實ResouseEncoder繼承自EncoderEncoder是用來持久化Data的,ResourceEncoder是用來持久化Resource的。看Glide默認注冊的Encoder就知道了,默認注冊的EncoderByteBufferInputStream,而ResourceEncoderBitmapBitmapDrawableGifDrawable,也就是一個持久化原始數據,一個持久化處理過的數據。我感覺把Encoder做為一個上級的抽象,引入一個和ResourceEncoder同級的DataEncoder就好理解了,正好和前面的基本概念DataResource對應。

      Encoder就有Decoder,對應的類叫ResourceDecoder,用來將數據(InputStream等)解析為Resource

      圖片加載出來后還可能會應用各種變換,如圓角圖片,圓形圖片,處理這部分工作的叫Transformation

      基礎概念介紹的差不多了,加載流程也差不多出來了:

      sequence1

      但我們發現前面的介紹中少了一環,即:Glide是怎么把Model轉換為Data的。這就引入另一個概念,ModelLoader,就是把Model轉換成Data的,為了方便說明,直接把這個類的代碼貼上來了,去掉了一些注釋。

      /**
      * A factory interface for translating an arbitrarily complex data model into a concrete data type
      * that can be used by an {@link DataFetcher} to obtain the data for a resource represented by the
      * model.
      *
      * @param <Model> The type of the model.
      * @param <Data>  The type of the data that can be used by a
      * {@link com.bumptech.glide.load.ResourceDecoder} to decode a resource.
      */
      public interface ModelLoader<Model, Data> {
      
       /**
        * Contains a set of {@link com.bumptech.glide.load.Key Keys} identifying the source of the load,
        * alternate cache keys pointing to equivalent data, and a
        * {@link com.bumptech.glide.load.data.DataFetcher} that can be used to fetch data not found in
        * cache.
        *
        * @param <Data> The type of data that well be loaded.
        */
       class LoadData<Data> {
         public final Key sourceKey;
         public final List<Key> alternateKeys;
         public final DataFetcher<Data> fetcher;
      
         public LoadData(Key sourceKey, DataFetcher<Data> fetcher) {
           this(sourceKey, Collections.<Key>emptyList(), fetcher);
         }
      
         public LoadData(Key sourceKey, List<Key> alternateKeys, DataFetcher<Data> fetcher) {
           this.sourceKey = Preconditions.checkNotNull(sourceKey);
           this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
           this.fetcher = Preconditions.checkNotNull(fetcher);
         }
       }
      
       LoadData<Data> buildLoadData(Model model, int width, int height, Options options);
      
       boolean handles(Model model);
      }
      

      ModelLoader有兩個方法,一個handles表示是否可以處理這個類型的Model,如果可以的話就可以通過buildLoadData生成一個LoadData,而LoadData包含了要用來做緩存的key,及用來獲取數據的DataFetcher

      到這里,整個加載流程就清楚了:

      sequence2

      基本加載流程

      接下來要做的就是根據我們的使用方法走一遍流程,調用如下:

      Glide.with(mContext)
          .load(url)
          .apply(RequestOptions.placeholderOf(R.drawable.loading))
          .into(myImageView);
      

      一步步看,先是Glide.with(mContext)

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

      通過RequestManagerRetriever獲取到了一個RequestManager,至于為什么還需要一個RequestManagerRetriever并有各種重載方法,主要是因為Glide通過SupportRequestManagerFragmentRequestManagerFragment關聯了Activity或Fragment的生命周期,用來做pauseRequests等操作。

      然后是load

      public RequestBuilder<Drawable> load(@Nullable Object model) {
       return asDrawable().load(model);
      }
      
      public RequestBuilder<Drawable> asDrawable() {
       return as(Drawable.class).transition(new DrawableTransitionOptions());
      }
      
      public <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) {
       return new RequestBuilder<>(glide.getGlideContext(), this, resourceClass);
      }
      

      asDrawable.load(model)的縮寫,就是說這個Model我是要加載為Drawable的,最終返回一個RequestBuilder,看名字就知道是做什么了,不過這個類主要是設置Thumbnail Request,Transition等個別設置(舊版本中placeHolder等也是在這里設置的),大部分設置在RequestOptions里,這就是下面這一句:

      apply(RequestOptions.placeholderOf(R.drawable.loading))
      

      應用一個RequestOptionsRequestOptions可以設置各種請求相關的選項,如占位圖片,加載失敗的圖片,緩存策略等。RequestOptions繼承自BaseRequestOptions,但全是工廠方法生成各種RequestOptions。

      最后就是into了,把圖片加載到一個Target中。

      public Target<TranscodeType> into(ImageView view) {
        ...
        return into(context.buildImageViewTarget(view, transcodeClass));
      }
      
      public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
       Util.assertMainThread();
       Preconditions.checkNotNull(target);
       if (!isModelSet) {
         throw new IllegalArgumentException("You must call #load() before calling #into()");
       }
      
       Request previous = target.getRequest();
      
       if (previous != null) {
         requestManager.clear(target);
       }
      
       requestOptions.lock();
       Request request = buildRequest(target);
       target.setRequest(request);
       requestManager.track(target, request);
      
       return target;
      }
      

      Target是要加載到的目標,比如ImageViewTargetAppWidgetTarget,在這里我們傳進來了一個ImageView,內部生成了一個DrawableImageViewTarget。這里最主要的操作是buildRequest然后交給RequestManagertrack

      void track(Target<?> target, Request request) {
       targetTracker.track(target);
       requestTracker.runRequest(request);
      }
      
      // RequestTracker
      public void runRequest(Request request) {
       requests.add(request);
       if (!isPaused) {
         request.begin();
       } else {
         pendingRequests.add(request);
       }
      }
      

      TargetTracker主要就是記錄一下所有正在加載的圖片的Target,所以加載行為是在RequestTracker.runRequest中的,runRequest先判斷是否是pause狀態(RequestManager設置),如果不是就直接調用Request.begin觸發加載,否則就回到pending隊列里等待resume。

      除了設置縮略圖的情景,使用的Request都是SingleRequest,看一下它的begin方法:

      public void begin() {
       stateVerifier.throwIfRecycled();
       startTime = LogTime.getLogTime();
       if (model == null) {
         if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
           width = overrideWidth;
           height = overrideHeight;
         }
         // Only log at more verbose log levels if the user has set a fallback drawable, because
         // fallback Drawables indicate the user expects null models occasionally.
         int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
         onLoadFailed(new GlideException("Received null model"), logLevel);
         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 (Log.isLoggable(TAG, Log.VERBOSE)) {
         logV("finished run method in " + LogTime.getElapsedMillis(startTime));
       }
      }
      

      加載邏輯是這幾行:

       if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
         onSizeReady(overrideWidth, overrideHeight);
       } else {
         target.getSize(this);
       }
      

      判斷下是否知道Target的大小,如果大小已知就調用onSizeReady,否則就調用target.getSize獲取它的大小,當成功獲取到大小后,會通過回調繼續調用onSizeReady,所以整個加載方法都是在onSizeReady里的。至于Target怎么獲取它的大小,那要看它的實現了,對于ImageViewTarget,是通過ViewTreeObserver.OnPreDrawListener等到View要測繪的時候就知道它的大小了。

      onSizeReady就是把操作轉移到了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,
         Options options,
         boolean isMemoryCacheable,
         boolean useUnlimitedSourceExecutorPool,
         ResourceCallback cb) {
       Util.assertMainThread();
       long startTime = LogTime.getLogTime();
      
       EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
           resourceClass, transcodeClass, options);
      
       EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
       if (cached != null) {
         cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
           logWithTimeAndKey("Loaded resource from cache", startTime, key);
         }
         return null;
       }
      
       EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
       if (active != null) {
         cb.onResourceReady(active, DataSource.MEMORY_CACHE);
         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<R> engineJob = engineJobFactory.build(key, isMemoryCacheable,
           useUnlimitedSourceExecutorPool);
       DecodeJob<R> decodeJob = decodeJobFactory.build(
           glideContext,
           model,
           key,
           signature,
           width,
           height,
           resourceClass,
           transcodeClass,
           priority,
           diskCacheStrategy,
           transformations,
           isTransformationRequired,
           options,
           engineJob);
       jobs.put(key, engineJob);
       engineJob.addCallback(cb);
       engineJob.start(decodeJob);
      
       if (Log.isLoggable(TAG, Log.VERBOSE)) {
         logWithTimeAndKey("Started new load", startTime, key);
       }
       return new LoadStatus(cb, engineJob);
      }
      

      Engine.load中,先loadFromCache,如果緩存沒有命中就再loadFromActiveResources,這是兩級內存緩存,第一級是LruCache,第二級是ActiveCache,主要作用是,有可能一個圖片很早就被加載了,可能已經從LruCache被移除掉了,但這個圖片可能還在被某一個地方引用著,也就是還是Active的,那它就可能在將來仍被引用到,所以就把它保留在二級的ActiveCache中,ActiveCache中是以弱引用引用圖片的,并通過ReferenceQueue監測弱引用的回收,然后用Handler.IdleHandler在CPU空閑時被被回收的引用項從ActiveCache中移除。

      接下來看對應的Key是否已經正在加載,如果是的話,就addCallback,這樣如果有多個地方同時請求同一張圖片的話,只會生成一個加載任務,并都能收到回調,這點是比Universal-Image-Loader好的地方。

      正常的加載流程是生成一個EngineJob和一個DecodeJob,通過engineJob.start(decodeJob)來進行實際的加載。

      public void start(DecodeJob<R> decodeJob) {
       this.decodeJob = decodeJob;
       GlideExecutor executor = decodeJob.willDecodeFromCache()
           ? diskCacheExecutor
           : getActiveSourceExecutor();
       executor.execute(decodeJob);
      }
      

      EngineJob.start直接將DecodeJob交給Executor去執行了(DecodeJob實現了Runnable接口)。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);
       }
      }
      
      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);
       }
      }
      
      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:
           return Stage.SOURCE;
         case SOURCE:
         case FINISHED:
           return Stage.FINISHED;
         default:
           throw new IllegalArgumentException("Unrecognized stage: " + current);
       }
      }
      

      主要加載邏輯就在這三個函數中了:

      1. 先獲取當前的Stage
      2. 根據當前的Stage獲取相應的Generator,
      3. 執行Generator

      一共有三種Generator:

      • ResourceCacheGenerator:從處理過的緩存加載數據
      • DataCacheGenerator:從原始緩存加載數據
      • SourceGenerator:從數據源請求數據,如網絡請求

      前面說過,Glide的磁盤緩存可以選擇緩存原始圖片,緩存處理過的圖片(如列表中顯示縮略圖時縮放后的圖片),這三個Generator就分別對應處理過的圖片緩存,原始圖片緩存,和數據源加載。

      在上面的第三步執行Generator時主要就是調用了Generator,其實就是執行Generator的startNext方法,這里以SourceGenerator為例。

      public boolean startNext() {
       if (dataToCache != null) {
         Object data = dataToCache;
         dataToCache = null;
         cacheData(data);
       }
      
       if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
         return true;
       }
       sourceCacheGenerator = null;
      
       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;
      }
      

      先忽略函數開始時dataToCachesourceCacheGenerator相關的代碼,第一次加載時這兩個一定是null的。剩下的流程就是獲取一個LoadData,調用LoadData.fetcher.loadData加載數據。看一下LoadData

      List<LoadData<?>> getLoadData() {
       if (!isLoadDataSet) {
         isLoadDataSet = true;
         loadData.clear();
         List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
         int size = modelLoaders.size();
         for (int i = 0; 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;
      }
      

      getLoadData中通過獲取所有提前注冊過的能處理Model類型的ModelLoader,調用它的buildLoadData生成LoadData,最終返回一個LoadData列表。

      前面說過LoadData包含了用來獲取數據的DataFetcherSourceGenerator.startNext就調用了loadData.fetcher.loadData來進行加載數據,并傳進去一個Callback,就是當前的SourceGenerator,如果加載成功,會調用onDataReady

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

      數據加載成功后,如果設置了要進行磁盤緩存,會設置成員變量dataToCache,并調用Callback的reschedule,結果就是會再次調用當前Generator的startNextstartNext的前半部分實現就起作用了,會進行寫緩存的操作。

      rescheudle后寫了緩存后,或不緩存的情況下,會調用onDataFetcherReady,這個Callback就是前面的DecodeJob,在onDataFetcherReady中會調用decodeFromRetrievedData decode數據,最終調用到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);
      }
      

      獲取LoadPath,并調用它的load方法。LoadPath就是封裝了多個DecodePathDecodePath用于decode and Transform數據,如InputStream->Bitmap->BitmapDrawable,DecodePath中會獲取預先注冊的Decoder來decode獲取到的數據,decode成功后通過回調調用DecodeJobonResourceDecoded方法。

      public Resource<Z> onResourceDecoded(Resource<Z> decoded) {
       Class<Z> resourceSubClass = getResourceClass(decoded);
       Transformation<Z> appliedTransformation = null;
       Resource<Z> transformed = decoded;
       if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
         appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
         transformed = appliedTransformation.transform(decoded, width, height);        //////////////////////////    1
       }
       // TODO: Make this the responsibility of the Transformation.
       if (!decoded.equals(transformed)) {
         decoded.recycle();
       }
      
       final EncodeStrategy encodeStrategy;
       final ResourceEncoder<Z> encoder;
       if (decodeHelper.isResourceEncoderAvailable(transformed)) {
         encoder = decodeHelper.getResultEncoder(transformed);
         encodeStrategy = encoder.getEncodeStrategy(options);
       } else {
         encoder = null;
         encodeStrategy = EncodeStrategy.NONE;
       }
      
       Resource<Z> result = transformed;
       boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
       if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
           encodeStrategy)) {
         if (encoder == null) {
           throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
         }
         final Key key;
         if (encodeStrategy == EncodeStrategy.SOURCE) {
           key = new DataCacheKey(currentSourceKey, signature);
         } else if (encodeStrategy == EncodeStrategy.TRANSFORMED) {
           key = new ResourceCacheKey(currentSourceKey, signature, width, height,
               appliedTransformation, resourceSubClass, options);
         } else {
           throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
         }
      
         LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
         deferredEncodeManager.init(key, encoder, lockedResult);           //////////////////////////    2
         result = lockedResult;
       }
       return result;
      }
      

      在上述代碼的注釋1處對加載成功的資源應用Transformation,然后在注釋2處根據緩存策略初始化DeferredEncodeManager,在前面的decodeFromRetrievedData中,如果有必要會把transform過的資源寫緩存。

      private void decodeFromRetrievedData() {
        ...
      
       if (resource != null) {
         notifyEncodeAndRelease(resource, currentDataSource);
       } else {
         runGenerators();
       }
      }
      

      notifyEncodeAndRelease中處理了對處理過的圖片的緩存操作。當緩存完成后(如果有需要的話)就通過回調告訴外面加載完成了。至此,整個加載過程完成。

      Glide配置

      Glide允許我們進行一定程度的自定義,比如設置自定義的Executor,設置緩存池,設置Log等級等,完成這個任務的類叫GlideBuilder,Glide類在工程中是作為單例使用的,看一下代碼:

      public static Glide get(Context context) {
       if (glide == null) {
         synchronized (Glide.class) {
           if (glide == null) {
             Context applicationContext = context.getApplicationContext();
             List<GlideModule> 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.registry);
             }
           }
         }
       }
      
       return glide;
      }
      

      通過GlideBuilder生成了一個Glide實例,我們是沒有辦法直接配置GlideBuilder的,但我們發現Glide.get解析了Manifest,獲取了一個GlideModule的列表,并調用了它的applyOptionsregisterComponents方法。以項目中OkHttp的配置為例

      public class OkHttpGlideModule implements GlideModule {
       @Override
       public void applyOptions(Context context, GlideBuilder builder) {
         // Do nothing.
       }
      
       @Override
       public void registerComponents(Context context, Registry registry) {
         registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
       }
      }
      

      GlideModule有兩個方法,applyOptions,有一個GlideBuilder參數,在這里我們就可以配置Glide了。還有一個registerComponents方法,并有一個Registry參數,通過這個類的實例我們就可以注冊我們自定義的ModelLoaderEncoder等基礎組件了。

      自定義GlideModule是通過Manifest的meta-data標簽配置的

      <meta-data
         android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"
         android:value="GlideModule"/>
      

      參考資料

      http://www.lightskystreet.com/2015/10/12/glide_source_analysis/

      posted @ 2016-09-05 13:41  AngelDevil  閱讀(2612)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 成人午夜无人区一区二区| 国产亚洲av夜间福利香蕉149| 亚洲日韩AV秘 无码一区二区| 被拉到野外强要好爽| 国产三级a三级三级| 国产亚洲欧洲AⅤ综合一区| 9久9久热精品视频在线观看| 久久综合色天天久久综合图片| 久治县| 日韩高清不卡一区二区三区| 国厂精品114福利电影免费| 亚洲欧美人成人综合在线播放| 国内精品久久久久影视| 国产亚洲精品岁国产精品| 蜜桃网址| 亚洲熟妇精品一区二区| 久久夜色精品国产亚av| 午夜福利看片在线观看| 精品国产污污免费网站| 韩国主播av福利一区二区| 91亚洲国产三上悠亚在线播放| 麻豆蜜桃av蜜臀av色欲av| 国产精品视频午夜福利| 亚洲日韩久热中文字幕| 日韩一区二区三区日韩精品| 99欧美日本一区二区留学生| 久久九九精品99国产精品| 色偷偷成人综合亚洲精品| 国产目拍亚洲精品二区| 无码粉嫩虎白一线天在线观看| 伊人成色综合人夜夜久久| 人妻少妇精品视频专区| 97香蕉碰碰人妻国产欧美| 91老熟女老人国产老太| 久久精品国产亚洲av麻豆软件| 国产精品久久久久久久久电影网| 久久日韩精品一区二区五区| 亚洲国模精品一区二区| 国产精品欧美福利久久| 日韩淫片毛片视频免费看| 日韩有码国产精品一区|