Android開源原始碼分析

ouclbc發表於2018-04-28

glide原始碼分析

李保成

glide是 Bump出品的一個圖片載入庫

開源地址:https://github.com/bumptech/glide

glide使用方法如下:

Glide.with(this).load("http://pic9.nipic.com/20100919/5123760_093408576078_2.jpg")
.into(mImageview);

效果如下:

Android開源原始碼分析

其總體架構如下:

Android開源原始碼分析

with

先看一下with方法:

public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}

作用:初始化glide,返回一個RequestManger物件

以傳入的是FragmentActivity為例:

getRetriever (activity)流程如下:

Android開源原始碼分析Android開源原始碼分析


initializeGlide主要做了以下工作:

1. 獲取應用中帶註解的GlideModule(annotationGeneratedModule),這裡要解釋一下GlideModule:使用者自定義glide配置模組,用來修改預設的glide配置資訊。如果這個為空或者可配置menifest裡面的標誌為true,則獲取menifest裡面配置的GlideModule模組(manifestModules)。

2. 把manifestModules以及annotationGeneratedModule裡面的配置資訊放到builder裡面(applyOptions)替換glide預設元件(registerComponents)

3. 各種初始化資訊Glide glide = builder.build(applicationContext);

4. 看一下build的原始碼:

主要做了以下工作:

1)建立請求圖片執行緒池sourceExecutor,建立硬碟快取執行緒池diskCacheExecutor。動畫執行緒池animationExecutor

2)依據裝置的螢幕密度和尺寸設定各種pool的size

3)建立圖片執行緒池LruBitmapPool,快取所有被釋放的bitmap, LruBitmapPool依賴預設的快取策略和快取配置。快取策略在API大於19時,為SizeConfigStrategy,小於為AttributeStrategy。其中SizeConfigStrategy是以bitmap的size和config為key,value為bitmap的HashMap。

4)建立物件陣列快取池LruArrayPool,預設4M

5)建立LruResourceCache,記憶體快取

6)註冊管理任務執行物件的類(Registry),可以簡單理解為:Registry是一個工廠,而其中所有註冊的物件都是一個工廠員工,當任務分發時,根據當前任務的性質,分發給相應員工進行處理。

Glide build(@NonNull
Context context) {

  if
(sourceExecutor == null)
{

    sourceExecutor
= GlideExecutor.newSourceExecutor();

  }



  if
(diskCacheExecutor == null)
{

    diskCacheExecutor
= GlideExecutor.newDiskCacheExecutor();

  }



  if
(animationExecutor == null)
{

    animationExecutor
= GlideExecutor.newAnimationExecutor();

  }



  if
(memorySizeCalculator == null)
{

    memorySizeCalculator
= new MemorySizeCalculator.Builder(context).build();

  }



  if
(connectivityMonitorFactory
== null) {

    connectivityMonitorFactory
= new DefaultConnectivityMonitorFactory();

  }



  if
(bitmapPool == null)
{

    int
size = memorySizeCalculator.getBitmapPoolSize();

    if (size
> 0)
{

      bitmapPool
= new LruBitmapPool(size);

    }
else {

      bitmapPool
= new BitmapPoolAdapter();

    }

  }
  if
(arrayPool == null)
{
    arrayPool= new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());

  }
  if
(memoryCache == null)
{
    memoryCache= new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
  }
  if
(diskCacheFactory == null)
{
    diskCacheFactory= new InternalCacheDiskCacheFactory(context);

  }
 if
(engine == null)
{
    engine=new Engine(
            memoryCache,
            diskCacheFactory,
            diskCacheExecutor,
            sourceExecutor,
            GlideExecutor.newUnlimitedSourceExecutor(),
            GlideExecutor.newAnimationExecutor(),
            isActiveResourceRetentionAllowed);
  }
  RequestManagerRetriever requestManagerRetriever =

      new RequestManagerRetriever(requestManagerFactory);
  return new Glide(
      context,
      engine,
      memoryCache,
      bitmapPool,
      arrayPool,
      requestManagerRetriever,
      connectivityMonitorFactory,
      logLevel,
      defaultRequestOptions.lock(),
      defaultTransitionOptions);

}複製程式碼

glide引數含義:

context上下文,engine:任務和資源管理(執行緒池,記憶體快取和硬碟快取物件

),memoryCache:記憶體快取,bitmapPool:bitmap記憶體快取,後續會單獨介紹。

arrayPool: connectivityMonitorFactory:回撥監聽,defaultRequestOptions:預設請求配置,

defaultTransitionOptions:預設過度效果

getRetriever(activity).get(activity)

然後我們看一下get(activity)的流程:

public RequestManager get(@NonNull FragmentActivity activity) {

  if (Util.isOnBackgroundThread())
{

    return get(activity.getApplicationContext());

  } else {

    assertNotDestroyed(activity);

    FragmentManager fm =
activity.getSupportFragmentManager();

    return supportFragmentGet(
        activity, fm, /*parentHint=*/
null,
isActivityVisible(activity));
  }
}複製程式碼

流程圖如下:

Android開源原始碼分析Android開源原始碼分析


主要工作是,建立一個supportFragment(SupportRequestManagerFragment),把Request和Fragment繫結在一起,主要是生命週期。

引用xiaodanchen.github.io/2016/08/19/…

文章中的一個圖片:

Android開源原始碼分析Android開源原始碼分析


總結以下with中的工作主要有以下幾點:

1. 初始化配置資訊(包括快取,請求執行緒池,大小,圖片格式等等)以及glide元件,

2. 將glide和Fragment的生命週期繫結在一塊。

Load

asDrawable().load(string);

先分析一下asDrawable:

Android開源原始碼分析

Android開源原始碼分析

最後是建立了一個RequestBuilder物件

private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {

this.model = model;

isModelSet = true;

return this;

}

最終返回RequestBuider物件

into


private <Y extends Target<TranscodeType>> Y into(

    @NonNull Y target,

    @Nullable RequestListener<TranscodeType>
targetListener,

    @NonNull RequestOptions
options) {

  Util.assertMainThread();

  Preconditions.checkNotNull(target);

  if (!isModelSet) {

    throw new IllegalArgumentException("You
must call #load() before calling #into()");

  }



  options = options.autoClone();

  Request request =
buildRequest(target, targetListener, options);



  Request previous =
target.getRequest();

  if (request.isEquivalentTo(previous)

      &&
!isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {

    request.recycle();

    // If the request is
completed, beginning again will ensure the result is re-delivered,

    // triggering RequestListeners and
Targets. If the request is failed, beginning again will

    // restart the request, giving it
another chance to complete. If the request is already

    // running, we can let it continue
running without interruption.

    if (!Preconditions.checkNotNull(previous).isRunning())
{

      // Use the previous
request rather than the new one to allow for optimizations like skipping

      // setting placeholders, tracking
and un-tracking Targets, and obtaining View dimensions

     
// that are done in the individual Request.

      previous.begin();

    }

    return target;

  }



  requestManager.clear(target);

  target.setRequest(request);

  requestManager.track(target, request);



  return target;

}


構建Request物件,實際是在buildRequestRecursive裡面建立了一個buildThumbnailRequestRecursive的物件以及errorRequestCoordinator(異常處理物件)


最後呼叫requestManager.track(target,
request);


track幹了兩件事:


void track(@NonNull Target<?> target, @NonNull Request
request) {

  targetTracker.track(target);

  requestTracker.runRequest(request);

}


1.     加入target目標佇列(view)


2.     加入請求Request佇列,如果快取中沒有開始請求資料


3. 
@Override

public void begin() {

  assertNotCallingCallbacks();

  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;

  }



  if
(status == Status.RUNNING) {

    throw
new IllegalArgumentException("Cannot restart a running request");

  }



  //
If we're restarted after we're complete (usually via something like a
notifyDataSetChanged

  // that starts an identical request
into the same Target or View), we can simply use the

  // resource and size we retrieved the
last time around and skip obtaining a new size, starting a

  // new load etc. This does mean that
users who want to restart a load because they expect that

  // the view size has changed will need
to explicitly clear the View or Target before starting

  // the new load.

  if
(status == Status.COMPLETE) {

    onResourceReady(resource, DataSource.MEMORY_CACHE);

    return;

  }



  //
Restarts for requests that are neither complete nor running can be treated as
new requests

  // and can run again from the
beginning.



  status
= Status.WAITING_FOR_SIZE;

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

    onSizeReady(overrideWidth, overrideHeight);

  }
else {

    target.getSize(this);

  }



  if
((status == Status.RUNNING || status ==
Status.WAITING_FOR_SIZE)

      &&
canNotifyStatusChanged()) {

    target.onLoadStarted(getPlaceholderDrawable());

  }

  if
(IS_VERBOSE_LOGGABLE) {

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

  }

}
複製程式碼

begin在onSizeReady執行engine.load, 先從弱引用中查詢loadFromActiveResources(),如果有的話直接返回,沒有再從記憶體中查詢loadFromCache, 有的話會取出並放到ActiveResources裡面,如果記憶體中沒有,則建立engineJob(decodejob的回撥類,管理下載過程以及狀態)執行緒decodeJob,先下載,後解析,並把Job放到Hashmap裡面。

流程圖如下:

Android開源原始碼分析Android開源原始碼分析


開啟執行緒是從engineJob.start開始的

Android開源原始碼分析

看一下DecodeJob執行緒的run方法,實際起作用的是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);
}
}

完整執行的情況下,會依次呼叫ResourceCacheGenerator、DataCacheGenerator和SourceGenerator中的startNext()

首次下載圖片建立的是SourceGenerator:

runGenerators流程如下:

private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();

if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}

// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
}

呼叫了SourceGenerator的startNext方法:

1、dataToCache資料不為空的話快取到硬碟(非第一次)

2、 在modelLoaders裡面找到ModelLoder物件(每個Generator對應一個ModelLoader)

3、通過(HttpGlideUrlLoader)buildLoadData獲取到實際的loadData物件(key 為URL,value建立的HttpUrlFetcher物件)

4、通過loadData物件的fetcher物件(HttpUrlFetcher)的loadData方法來獲取圖片資料。

5、HttpUrlFetcher 通過HttpURLConnection網路請求資料

時序圖如下:

Android開源原始碼分析

至此,算是部分完整的分析了glide的過程,後續會繼續完善。

Android開源原始碼分析

參考:

blog.csdn.net/guolin_blog…

xiaodanchen.github.io/2016/08/22/…

blog.csdn.net/zsago/artic…

掘金的格式還不怎麼會調整,看格式完整的可以去我的github

github.com/ouclbc/Anal…



相關文章