一级毛片免费不卡在线视频,国产日批视频免费在线观看,菠萝菠萝蜜在线视频免费视频,欧美日韩亚洲无线码在线观看,久久精品这里精品,国产成人综合手机在线播放,色噜噜狠狠狠综合曰曰曰,琪琪视频

Android Glide源碼解析 -電腦資料

電腦資料 時(shí)間:2019-01-01 我要投稿
【www.oriental01.com - 電腦資料】

   

功能介紹

    使用文章介紹以及和Picasso的對(duì)比分析請(qǐng)參考Introduction to Glide, Image Loader Library for Android, recommended by Google

    由于這篇文章使用glide的老版本,因此有些使用方法可能不太一致了,

Android Glide源碼解析

。

    本文基于github上Glide最新代碼4.0.0版本做解析。

    最基本的使用方式如下:

<code avrasm="" class="hljs">Glide.with(this)                .asDrawable()                .load(http://i6.topit.me/6/5d/45/1131907198420455d6o.jpg)                .apply(fitCenterTransform(this))                .apply(placeholderOf(R.drawable.skyblue_logo_wechatfavorite_checked))                .into(imageView);</code>

    Glide使用了現(xiàn)在非常流行的流氏編碼方式,方便了開(kāi)發(fā)者的使用,簡(jiǎn)明、扼要。

    接下來(lái)主要對(duì)上面這一段流氏操作做拆分。

Glide 主入口

    這個(gè)類(lèi)有點(diǎn)像門(mén)臉模式的統(tǒng)一代理入口,不過(guò)實(shí)際作用在4.0.0中很多功能都被放到后面的其他類(lèi)中,此類(lèi)關(guān)注的點(diǎn)就很少了。雖然整個(gè)libray的所有需要的組建都在這個(gè)類(lèi)中,但是實(shí)際也只是一個(gè)統(tǒng)一初始化的地方。

RequestManager(Glide.with(…))

    這個(gè)類(lèi)主要用于管理和啟動(dòng)Glide的所有請(qǐng)求,可以使用activity,fragment或者連接生命周期的事件去智能的停止,啟動(dòng),和重啟請(qǐng)求。也可以檢索或者通過(guò)實(shí)例化一個(gè)新的對(duì)象,或者使用靜態(tài)的Glide去利用構(gòu)建在Activity和Fragment生命周期處理中。它的方法跟你的Fragment和Activity的是同步的。

RequestBuilder

    通用類(lèi),可以處理設(shè)置選項(xiàng),并啟動(dòng)負(fù)載的通用資源類(lèi)型。

    在這個(gè)類(lèi)中,主要是應(yīng)用請(qǐng)求的很多選項(xiàng)(如下的選項(xiàng)從字面都能看出具體的用處,在ImageView控件中經(jīng)常也能看到,另外之前版本可不是這么使用的):

<code class="hljs" php="">public final class RequestOptions extends BaseRequestOptions<requestoptions>{  private static RequestOptions skipMemoryCacheTrueOptions;  private static RequestOptions skipMemoryCacheFalseOptions;  private static RequestOptions fitCenterOptions;  private static RequestOptions centerCropOptions;  private static RequestOptions circleCropOptions;  private static RequestOptions noTransformOptions;  private static RequestOptions noAnimationOptions;  // ...省略...}</requestoptions></code>

    RequestBuilder transition(TransitionOptions transitionOptions){} 這個(gè)方法主要是用于加載對(duì)象從占位符(placeholder)或者縮略圖(thumbnail)到真正對(duì)象加載完成的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)。

    RequestBuilder load(…){}多太方法中,這里可以加載很多類(lèi)型的數(shù)據(jù)對(duì)象,可以是String,Uri,F(xiàn)ile,resourceId,byte[]這些。當(dāng)然這些后面對(duì)應(yīng)的編碼方式也是不一樣的。

    Target into(…){}這個(gè)方法是觸發(fā)Request真正啟動(dòng)的地方,在上邊的示例中最后就是調(diào)用這個(gè)方法發(fā)起請(qǐng)求。

    不得不說(shuō)的registry域,這個(gè)域掛載了很多元件,該注冊(cè)器中囊括了模塊加載器(ModelLoader)、編碼器(Encoder)、資源解碼器(ResourceDecoder)、資源編碼器(ResourceEncoder)、數(shù)據(jù)回轉(zhuǎn)器(DataRewinder)、轉(zhuǎn)碼器(Transcoder)。這些都是Glide在對(duì)資源編解碼中既是基礎(chǔ)又是核心功能。

代碼結(jié)構(gòu)

    這里主要列舉一下一些重要的組件以及他們的結(jié)構(gòu)關(guān)系:

    ModelLoader

   

    DataFetcher

   

    Target

   

    Resource

   

    ResourceTransformation

   

    Pool

   

    Cache

   

    Decoder

   

    Encoder

   

    把這些組件代碼結(jié)構(gòu)列舉出來(lái)主要是為了讓讀者和使用者一目了然的看到自己需要的一些功能。

執(zhí)行流程

1、根據(jù)不同版本的Fragment創(chuàng)建RequestManagerFragment或者SupportRequestManagerFragment,并加入到對(duì)應(yīng)的FragmentManager中。這兩種Fragment是不帶有任何界面的,主要是用于同步生命周期。具體實(shí)現(xiàn)如下:

<code avrasm="" class="hljs">public static RequestManager with(Context context) {    RequestManagerRetriever retriever = RequestManagerRetriever.get();    return retriever.get(context);  }// RequestManagerRetriever.get(...)  @TargetApi(Build.VERSION_CODES.HONEYCOMB)  public RequestManager get(Activity activity) {    if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {      return get(activity.getApplicationContext());    } else {      assertNotDestroyed(activity);      android.app.FragmentManager fm = activity.getFragmentManager();      return fragmentGet(activity, fm, null);    }  }  @TargetApi(Build.VERSION_CODES.HONEYCOMB)  RequestManager fragmentGet(Context context, android.app.FragmentManager fm,      android.app.Fragment parentHint) {    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);    RequestManager requestManager = current.getRequestManager();    if (requestManager == null) {      requestManager =          new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());      current.setRequestManager(requestManager);    }    return requestManager;  }  @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)  RequestManagerFragment getRequestManagerFragment(      final android.app.FragmentManager fm, android.app.Fragment parentHint) {    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);    if (current == null) {      current = pendingRequestManagerFragments.get(fm);      if (current == null) {        current = new RequestManagerFragment();        current.setParentFragmentHint(parentHint);        pendingRequestManagerFragments.put(fm, current);        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();      }    }    return current;  }</code>

2、創(chuàng)建一個(gè)RequestBuilder,并添加一個(gè)DrawableTransitionOptions類(lèi)型的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)

<code class="hljs" cs="">public RequestBuilder<drawable>asDrawable() {    return as(Drawable.class).transition(new DrawableTransitionOptions());  }</drawable></code>

3、加載對(duì)象(model域)

<code class="hljs" cs="">public RequestBuilder<transcodetype>load(@Nullable Object model) {    return loadGeneric(model);  }private RequestBuilder<transcodetype>loadGeneric(@Nullable Object model) {    this.model = model;    isModelSet = true;    return this;  }</transcodetype></transcodetype></code>

4、裝載對(duì)象(包含請(qǐng)求的發(fā)起點(diǎn))。

<code class="hljs" cs="">public<y extends="" 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;  }</y></code>

    一般而言,大部分使用者都是用來(lái)裝載圖片的,因此都會(huì)調(diào)用如下這個(gè)方法:

<code class="hljs" cs="">public Target<transcodetype>into(ImageView view) {    Util.assertMainThread();    Preconditions.checkNotNull(view);    if (!requestOptions.isTransformationSet()        && requestOptions.isTransformationAllowed()        && view.getScaleType() != null) {      if (requestOptions.isLocked()) {        requestOptions = requestOptions.clone();      }      switch (view.getScaleType()) {        case CENTER_CROP:          requestOptions.optionalCenterCrop(context);          break;        case FIT_CENTER:        case FIT_START:        case FIT_END:          requestOptions.optionalFitCenter(context);          break;        //$CASES-OMITTED$        default:          // Do nothing.      }    }    return into(context.buildImageViewTarget(view, transcodeClass));  }</transcodetype></code>

    這里針對(duì)ImageView的填充方式做了篩選并對(duì)應(yīng)設(shè)置到requestOptions上。最終的是通過(guò)ImageView和轉(zhuǎn)碼類(lèi)型(transcodeClass)創(chuàng)建不通過(guò)的Target(例如Bitmap對(duì)應(yīng)的BitmapImageViewTarget和Drawable對(duì)應(yīng)的DrawableImageViewTarget)

    4.1 Request的創(chuàng)建buildRequest(target)。

    在Request的創(chuàng)建中會(huì)針對(duì)是否有縮略圖來(lái)創(chuàng)建不同尺寸的請(qǐng)求,縮略圖方法可以使用RequestBuilder.thumbnail(…)方法來(lái)添加上。

    Glide中的Request都是使用了SingleRequest類(lèi),當(dāng)然縮略圖采用的是ThumbnailRequestCoordinator類(lèi):

<code class="hljs" cs="">private Request obtainRequest(Target<transcodetype>target,      BaseRequestOptionsrequestOptions, RequestCoordinator requestCoordinator,      TransitionOptionstransitionOptions, Priority priority,      int overrideWidth, int overrideHeight) {    requestOptions.lock();    return SingleRequest.obtain(        context,        model,        transcodeClass,        requestOptions,        overrideWidth,        overrideHeight,        priority,        target,        requestListener,        requestCoordinator,        context.getEngine(),        transitionOptions.getTransitionFactory());  }</transcodetype></code>

    比較值得推崇的是SingleRequest.obtain寫(xiě)法,個(gè)人認(rèn)為比new關(guān)鍵字更簡(jiǎn)潔明了吧。

    target.setRequest(request)也是一個(gè)比較值得注意的地方,如果target是ViewTarget,那么request會(huì)被設(shè)置到View的tag上。這樣其實(shí)是有一個(gè)好處,每一個(gè)View有一個(gè)自己的Request,如果有重復(fù)請(qǐng)求,那么都會(huì)先去拿到上一個(gè)已經(jīng)綁定的Request,并且從RequestManager中清理回收掉。這應(yīng)該是去重的功能。

    4.2 requestManager.track(target, request)

    這個(gè)方法非常的復(fù)雜,主要用于觸發(fā)請(qǐng)求、編解碼、裝載和緩存這些功能。下面就一步一步來(lái)看吧:

    4.2.1 緩存target,并啟動(dòng)Request

<code class="hljs" java="">void track(Targettarget, Request request) {    targetTracker.track(target);    requestTracker.runRequest(request);  }  /**   * Starts tracking the given request.   */  public void runRequest(Request request) {    requests.add(request); //添加內(nèi)存緩存    if (!isPaused) {      request.begin(); // 開(kāi)始    } else {      pendingRequests.add(request); // 掛起請(qǐng)求    }  }</code>

    繼續(xù)看一下SingleRequest中的begin方法:

<code class="hljs" java="">@Override  public void begin() {    stateVerifier.throwIfRecycled();    startTime = LogTime.getLogTime();    // 如果model空的,那么是不能執(zhí)行的。 這里的model就是前面提到的RequestBuilder中的model    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;    // 如果當(dāng)前的View尺寸已經(jīng)加載獲取到了,那么就會(huì)進(jìn)入真正的加載流程。    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {      onSizeReady(overrideWidth, overrideHeight);    } else {    // 反之,當(dāng)前View還沒(méi)有畫(huà)出來(lái),那么是沒(méi)有尺寸的。    // 這里會(huì)調(diào)用到ViewTreeObserver.addOnPreDrawListener。    // 等待View的尺寸都o(jì)k,才會(huì)繼續(xù)      target.getSize(this);    }    // 如果等待和正在執(zhí)行狀態(tài),那么當(dāng)前會(huì)加載占位符Drawable    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));    }  }</code>

    接下來(lái)是target.getSize(this)方法。這里主要說(shuō)一下尺寸未加載出來(lái)的情況(ViewTarget.java):

<code class="hljs" java="">void getSize(SizeReadyCallback cb) {      int currentWidth = getViewWidthOrParam();      int currentHeight = getViewHeightOrParam();      if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {        cb.onSizeReady(currentWidth, currentHeight);      } else {        // We want to notify callbacks in the order they were added and we only expect one or two        // callbacks to        // be added a time, so a List is a reasonable choice.        if (!cbs.contains(cb)) {          cbs.add(cb);        }        if (layoutListener == null) {          final ViewTreeObserver bserver = view.getViewTreeObserver();          layoutListener = new SizeDeterminerLayoutListener(this);          // 繪畫(huà)之前加入尺寸的監(jiān)聽(tīng)。這一點(diǎn)我想大部分Android開(kāi)發(fā)同學(xué)應(yīng)該都知道。          // 接下來(lái)在看看系統(tǒng)觸發(fā)該Listener時(shí)target又干了些什么。          observer.addOnPreDrawListener(layoutListener);        }      }    }private static class SizeDeterminerLayoutListener implements ViewTreeObserver        .OnPreDrawListener {        // 注意這里是弱引用      private final WeakReference<sizedeterminer>sizeDeterminerRef;      public SizeDeterminerLayoutListener(SizeDeterminer sizeDeterminer) {        sizeDeterminerRef = new WeakReference<>(sizeDeterminer);      }      @Override      public boolean onPreDraw() {        if (Log.isLoggable(TAG, Log.VERBOSE)) {          Log.v(TAG, OnGlobalLayoutListener called listener= + this);        }        SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();        if (sizeDeterminer != null) {         // 通知SizeDeterminer去重新檢查尺寸,并觸發(fā)后續(xù)操作。         // SizeDeterminer有點(diǎn)像工具類(lèi),又作為尺寸回調(diào)的檢測(cè)接口          sizeDeterminer.checkCurrentDimens();        }        return true;      }    }</sizedeterminer></code>

    ok,繼續(xù)回到SingleRequest.onSizeReady方法,主要就是Engine發(fā)起load操作

<code avrasm="" class="hljs">public void onSizeReady(int width, int height) {    stateVerifier.throwIfRecycled();    if (Log.isLoggable(TAG, Log.VERBOSE)) {      logV(Got onSizeReady in  + LogTime.getElapsedMillis(startTime));    }    if (status != Status.WAITING_FOR_SIZE) {      return;    }    status = Status.RUNNING;    float sizeMultiplier = requestOptions.getSizeMultiplier();    this.width = Math.round(sizeMultiplier * width);    this.height = Math.round(sizeMultiplier * height);    if (Log.isLoggable(TAG, Log.VERBOSE)) {      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.getOptions(),        requestOptions.isMemoryCacheable(),        this);    if (Log.isLoggable(TAG, Log.VERBOSE)) {      logV(finished onSizeReady in  + LogTime.getElapsedMillis(startTime));    }  }</code>

    特別的,所有的操作都是來(lái)之唯一一個(gè)Engine,它的創(chuàng)建是來(lái)至于Glide的初始化。如果有需要修改緩存配置的同學(xué)可以繼續(xù)看一下diskCacheFactory的創(chuàng)建:

<code class="hljs" cs="">if (engine == null) {      engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor);    }</code>

    繼續(xù)看一下Engine.load的詳細(xì)過(guò)程:

<code class="hljs" lasso="">public<r>LoadStatus load(      GlideContext glideContext,      Object model,      Key signature,      int width,      int height,      ClassresourceClass,      Class<r>transcodeClass,      Priority priority,      DiskCacheStrategy diskCacheStrategy,      Map<class<?>, Transformation> transformations,      boolean isTransformationRequired,      Options options,      boolean isMemoryCacheable,      ResourceCallback cb) {    Util.assertMainThread();    long startTime = LogTime.getLogTime();    // 創(chuàng)建key,這是給每次加載資源的唯一標(biāo)示,

電腦資料

Android Glide源碼解析》(http://www.oriental01.com)。 EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); // 通過(guò)key查找緩存資源 (PS 這里的緩存主要是內(nèi)存中的緩存,切記,可以查看MemoryCache) EngineResourcecached = loadFromCache(key, isMemoryCacheable); if (cached != null) { // 如果有,那么直接利用當(dāng)前緩存的資源。 cb.onResourceReady(cached, DataSource.MEMORY_CACHE); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey(Loaded resource from cache, startTime, key); } return null; } // 這是一個(gè)二級(jí)內(nèi)存的緩存引用,很簡(jiǎn)單用了一個(gè)Map<key,>>>裝載起來(lái)的。 // 這個(gè)緩存主要是誰(shuí)來(lái)放進(jìn)去呢? 可以參考上面一級(jí)內(nèi)存緩存loadFromCache方法。 EngineResourceactive = 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; } // 根據(jù)key獲取緩存的job。 EngineJob current = jobs.get(key); if (current != null) { current.addCallback(cb); // 給當(dāng)前job添加上回調(diào)Callback if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey(Added to existing load, startTime, key); } return new LoadStatus(cb, current); } // 創(chuàng)建job EngineJob<r>engineJob = engineJobFactory.build(key, isMemoryCacheable); 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); // 放入線(xiàn)程池,執(zhí)行 engineJob.start(decodeJob); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey(Started new load, startTime, key); } return new LoadStatus(cb, engineJob); }</r></r></key,></class<?></r></r></code>

    上面有一些值得注意的地方:

內(nèi)存緩存:在Glide中默認(rèn)是LruResourceCache。當(dāng)然你也可以自定義; 為何要兩級(jí)內(nèi)存緩存(loadFromActiveResources)。個(gè)人理解是一級(jí)緩存采用LRU算法進(jìn)行緩存,并不能保證全部能命中,添加二級(jí)緩存提高命中率之用; EngineJob和DecodeJob各自職責(zé):EngineJob充當(dāng)了管理和調(diào)度者,主要負(fù)責(zé)加載和各類(lèi)回調(diào)通知;DecodeJob是真正干活的勞動(dòng)者,這個(gè)類(lèi)實(shí)現(xiàn)了Runnable接口。

    下面來(lái)看看DecodeJob是如何執(zhí)行的:

<code class="hljs" cs="">private void runWrapped() {     switch (runReason) {      case INITIALIZE:        // 初始化 獲取下一個(gè)階段狀態(tài)        stage = getNextStage(Stage.INITIALIZE);        currentGenerator = getNextGenerator();        // 運(yùn)行        runGenerators();        break;      case SWITCH_TO_SOURCE_SERVICE:        runGenerators();        break;      case DECODE_DATA:        decodeFromRetrievedData();        break;      default:        throw new IllegalStateException(Unrecognized run reason:  + runReason);    }  }// 這里的階段策略首先是從resource中尋找,然后再是data,,再是sourceprivate Stage getNextStage(Stage current) {    switch (current) {      case INITIALIZE:       // 根據(jù)定義的緩存策略來(lái)回去下一個(gè)狀態(tài)      // 緩存策略來(lái)之于RequestBuilder的requestOptions域      // 如果你有自定義的策略,可以調(diào)用RequestBuilder.apply方法即可      // 詳細(xì)的可用緩存策略請(qǐng)參看DiskCacheStrategy.java        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);    }// 根據(jù)Stage找到數(shù)據(jù)抓取生成器。private DataFetcherGenerator getNextGenerator() {    switch (stage) {      case RESOURCE_CACHE:       // 產(chǎn)生含有降低采樣/轉(zhuǎn)換資源數(shù)據(jù)緩存文件的DataFetcher。        return new ResourceCacheGenerator(decodeHelper, this);      case DATA_CACHE:       // 產(chǎn)生包含原始未修改的源數(shù)據(jù)緩存文件的DataFetcher。        return new DataCacheGenerator(decodeHelper, this);      case SOURCE:      // 生成使用注冊(cè)的ModelLoader和加載時(shí)提供的Model獲取源數(shù)據(jù)規(guī)定的DataFetcher。      // 根據(jù)不同的磁盤(pán)緩存策略,源數(shù)據(jù)可首先被寫(xiě)入到磁盤(pán),然后從緩存文件中加載,而不是直接返回。        return new SourceGenerator(decodeHelper, this);      case FINISHED:        return null;      default:        throw new IllegalStateException(Unrecognized stage:  + stage);    }  }</code>

    經(jīng)過(guò)很多流程,最后來(lái)到了發(fā)起實(shí)際請(qǐng)求的地方SourceGenerator.startNext()方法:

<code class="hljs" java="">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;    // 查找ModelLoader    while (!started && hasNextModelLoader()) {      loadData = helper.getLoadData().get(loadDataListIndex++);      if (loadData != null          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {        started = true;        根據(jù)model的fetcher加載數(shù)據(jù)        loadData.fetcher.loadData(helper.getPriority(), this);      }    }    return started;  }</code>

    這里的Model必須是實(shí)現(xiàn)了GlideModule接口的,fetcher是實(shí)現(xiàn)了DataFetcher接口。有興趣同學(xué)可以繼續(xù)看一下integration中的okhttp和volley工程。Glide主要采用了這兩種網(wǎng)絡(luò)libray來(lái)下載圖片。

    4.2.2 數(shù)據(jù)下載完成后的緩存處理SourceGenerator.onDataReady

<code class="hljs" lasso="">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);    }  }</code>

    有些小伙伴可能看不太明白為什么就一個(gè)dataToCache = data就完了…其實(shí)cb.reschedule()很重要,這里的cb就是DecodeJob.reschedule():

<code class="hljs" cs="">public void reschedule() {    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;    callback.reschedule(this);  }</code>

    這里又有一個(gè)Callback,繼續(xù)追蹤,這里的Callback接口是定義在DecodeJob內(nèi)的,而實(shí)現(xiàn)是在外部的Engine中(這里會(huì)用線(xiàn)程池重新啟動(dòng)當(dāng)前job,那為什么要這樣做呢?源碼中的解釋是為了不同線(xiàn)程的切換,因?yàn)橄螺d都是借用第三方網(wǎng)絡(luò)庫(kù),而實(shí)際的編解碼是在Glide自定義的線(xiàn)程池中進(jìn)行的):

<code class="hljs" cs="">public void reschedule(DecodeJobjob) {    if (isCancelled) {      MAIN_THREAD_HANDLER.obtainMessage(MSG_CANCELLED, this).sendToTarget();    } else {      sourceExecutor.execute(job);    }  }</code>

    接下來(lái)繼續(xù)DecodeJob.runWrapped()方法。這個(gè)時(shí)候的runReason是SWITCH_TO_SOURCE_SERVICE,因此直接執(zhí)行runGenerators(),這里繼續(xù)執(zhí)行SourceGenerator.startNext()方法,值得注意的dataToCache域,因?yàn)樯弦淮螆?zhí)行的時(shí)候是下載,因此再次執(zhí)行的時(shí)候內(nèi)存緩存已經(jīng)存在,因此直接緩存數(shù)據(jù)cacheData(data):

<code class="hljs" cs="">private void cacheData(Object dataToCache) {    long startTime = LogTime.getLogTime();    try {    // 根據(jù)不同的數(shù)據(jù)獲取注冊(cè)的不同Encoder      Encoder<object>encoder = helper.getSourceEncoder(dataToCache);      DataCacheWriter<object>writer =          new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());      riginalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());      // 這里的DiskCache實(shí)現(xiàn)是Engine中LazyDiskCacheProvider提供的DiskCacheAdapter。      helper.getDiskCache().put(originalKey, writer);      if (Log.isLoggable(TAG, Log.VERBOSE)) {        Log.v(TAG, Finished encoding source to cache            + , key:  + originalKey            + , data:  + dataToCache            + , encoder:  + encoder            + , duration:  + LogTime.getElapsedMillis(startTime));      }    } finally {      loadData.fetcher.cleanup();    }    // 創(chuàng)建針對(duì)緩存的Generator    sourceCacheGenerator =        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);  }</object></object></code>

    繼續(xù)回到SourceGenerator.startNext()方法,這個(gè)時(shí)候已經(jīng)有了sourceCacheGenerator,那么直接執(zhí)行DataCacheGenerator.startNext()方法:

<code class="hljs" cs="">public boolean startNext() {    while (modelLoaders == null || !hasNextModelLoader()) {      sourceIdIndex++;      if (sourceIdIndex >= cacheKeys.size()) {        return false;      }      Key sourceId = cacheKeys.get(sourceIdIndex);      Key riginalKey = new DataCacheKey(sourceId, helper.getSignature());      cacheFile = helper.getDiskCache().get(originalKey);      if (cacheFile != null) {        this.sourceKey = sourceId;        modelLoaders = helper.getModelLoaders(cacheFile);        modelLoaderIndex = 0;      }    }    loadData = null;    boolean started = false;    // 這里會(huì)通過(guò)model尋找注冊(cè)過(guò)的ModelLoader    while (!started && hasNextModelLoader()) {      ModelLoader<file,>modelLoader = modelLoaders.get(modelLoaderIndex++);      loadData =          modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),              helper.getOptions());      // 通過(guò)FileLoader繼續(xù)加載數(shù)據(jù)      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {        started = true;        loadData.fetcher.loadData(helper.getPriority(), this);      }    }    return started;  }</file,></code>

    這里的ModelLoader跟之前提到過(guò)的Register的模塊加載器(ModelLoader)對(duì)應(yīng)是modelLoaderRegistry域,具體執(zhí)行的操作是Registry.getModelLoaders(…)方法如下:

<code class="hljs" php="">public<model>List<modelloader<model,>> getModelLoaders(Model model) {    List<modelloader<model,>> result = modelLoaderRegistry.getModelLoaders(model);    if (result.isEmpty()) {      throw new NoModelLoaderAvailableException(model);    }    return result;  }</modelloader<model,></modelloader<model,></model></code>

    繼續(xù)回到DataCacheGenerator.startNext()方法,找到了ModelLoader,這里筆者跟蹤到的是FileLoader類(lèi)(FileFetcher.loadData(…)方法):

<code class="hljs" lasso="">public void loadData(Priority priority, DataCallbackcallback) {        // 讀取文件數(shù)據(jù)      try {        data = opener.open(file);      } catch (FileNotFoundException e) {        if (Log.isLoggable(TAG, Log.DEBUG)) {          Log.d(TAG, Failed to open file, e);        }        //失敗        callback.onLoadFailed(e);        return;      }      // 成功      callback.onDataReady(data);    }</code>

    4.2.3 裝載流程

    回調(diào)通知這里就不打算多講了,主要線(xiàn)路如下:

<code class="hljs" haml="">-->DataCacheGenerator.onDataReady  -->SourceGenerator.onDataFetcherReady    -->DecodeJob.onDataFetcherReady    -->DecodeJob.decodeFromRetrievedData    -->DecodeJob.notifyEncodeAndRelease    -->DecodeJob.notifyComplete      -->EngineJob.onResourceReady</code>

    Debug流程圖:

   

    需要說(shuō)明的就是在EngineJob中有一個(gè)Handler叫MAIN_THREAD_HANDLER。為了實(shí)現(xiàn)在主UI中裝載資源的作用,ok繼續(xù)上邊的流程:

<code class="hljs" haml="">-->EngineJob.handleResultOnMainThread        -->SingleRequest.onResourceReady          -->ImageViewTarget.onResourceReady          -->ImageViewTarget.setResource            -->ImageView.setImageDrawable/ImageView.setImageBitmap</code>

    Debug流程圖2:

   

    數(shù)據(jù)的裝載過(guò)程中有一個(gè)很重要的步驟就是decode,這個(gè)操作發(fā)生在DecodeJob.decodeFromRetrievedData的時(shí)候,繼續(xù)看代碼:

<code class="hljs" cs="">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);      exceptions.add(e);    }    if (resource != null) {      notifyEncodeAndRelease(resource, currentDataSource);    } else {      runGenerators();    }  }</r></code>

    這中間發(fā)生了很多轉(zhuǎn)換主要流程:

<code class="hljs" haml="">-->DecodeJob.decodeFromData-->DecodeJob.decodeFromFetcher-->DecodeJob.runLoadPath  -->LoadPath.load  -->LoadPath.loadWithExceptionList  -->LoadPath.decode  -->LoadPath.decodeResource  -->LoadPath.decodeResourceWithList    -->ResourceDecoder.handles    -->ResourceDecoder.decode</code>

    這里講到了decode,那么encode發(fā)生在什么時(shí)候呢?直接通過(guò)Encoder接口調(diào)用發(fā)現(xiàn),在數(shù)據(jù)緩存的時(shí)候才會(huì)觸發(fā)編碼。具體調(diào)用在DiskLruCacheWrapper和DataCacheWriter中。一些值得參考的寫(xiě)法例如BitmapEncoder對(duì)Bitmap的壓縮處理。

結(jié)束語(yǔ)

    最近看開(kāi)源庫(kù)Glide關(guān)注度一直比較高,因此打算一探究竟。 由于時(shí)間比較緊,因此一些應(yīng)該有的時(shí)序圖沒(méi)有畫(huà),這里也只能簡(jiǎn)單用箭頭代替。不過(guò)個(gè)人認(rèn)為整體執(zhí)行流程已經(jīng)表達(dá)清楚了。

總體來(lái)說(shuō)代碼寫(xiě)的挺漂亮的,單從使用者角度來(lái)說(shuō)入手是比較容易的。 源碼使用了大量的工廠(chǎng)方法來(lái)創(chuàng)建對(duì)象,就像String.valueof(…)方法一樣,這也體現(xiàn)編碼的優(yōu)雅。 不過(guò)想要對(duì)這個(gè)庫(kù)進(jìn)行改造,可能并非易事,筆者在跟蹤代碼的過(guò)程中發(fā)現(xiàn)很多地方有Callback這樣的接口,來(lái)來(lái)回回查找?guī)状魏苋菀拙蜁烆^轉(zhuǎn)向了。。。 另外一個(gè)感覺(jué)難受的地方就是構(gòu)造方法帶入?yún)?shù)太多,就拿SingleRequest來(lái)說(shuō)就是12個(gè)構(gòu)造參數(shù)。 單例的使用感覺(jué)還是有些模糊,就比如GlideContext,有些時(shí)候通過(guò)Glide.get(context).getGlideContext()獲取,而有些類(lèi)中采用構(gòu)造傳入。個(gè)人覺(jué)得既然讓Glide作為單例,那么還這樣傳入?yún)?shù)是不是有點(diǎn)多余?代碼的編寫(xiě)都是可以折中考慮,不過(guò)如果整個(gè)項(xiàng)目擬定好了一個(gè)規(guī)則的話(huà),我想最好還是遵循它。另外再吐槽一下單例,很多開(kāi)發(fā)人員喜歡用單例,如果你是有代碼潔癖的開(kāi)發(fā)者,那么你肯定很討厭這樣,單例很容易造成代碼的散落和結(jié)構(gòu)不清晰。

思考

    源碼的解析只是把最重要的加載流程走了一遍,有一些比較細(xì)節(jié)的地方?jīng)]有關(guān)注,如果你有需要,可以自己跟著這個(gè)主線(xiàn)debug一下就能查找到。

最新文章