glide原始碼分析
李保成
glide是 Bump出品的一個圖片載入庫
開源地址:https://github.com/bumptech/glide
glide使用方法如下:
Glide.with(this).load("http://pic9.nipic.com/20100919/5123760_093408576078_2.jpg")
.into(mImageview);
效果如下:
其總體架構如下:
with
先看一下with方法:
public static RequestManager with(@NonNull FragmentActivity
activity) {
return getRetriever(activity).get(activity);
}
作用:初始化glide,返回一個RequestManger物件
以傳入的是FragmentActivity為例:
getRetriever (activity)流程如下:
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));
}
}複製程式碼
流程圖如下:
主要工作是,建立一個supportFragment(SupportRequestManagerFragment),把Request和Fragment繫結在一起,主要是生命週期。
引用xiaodanchen.github.io/2016/08/19/…
文章中的一個圖片:
總結以下with中的工作主要有以下幾點:
1. 初始化配置資訊(包括快取,請求執行緒池,大小,圖片格式等等)以及glide元件,
2. 將glide和Fragment的生命週期繫結在一塊。
Load
asDrawable().load(string);
先分析一下asDrawable:
最後是建立了一個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裡面。
流程圖如下:
開啟執行緒是從engineJob.start開始的
看一下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網路請求資料
時序圖如下:
至此,算是部分完整的分析了glide的過程,後續會繼續完善。
參考:
xiaodanchen.github.io/2016/08/22/…
掘金的格式還不怎麼會調整,看格式完整的可以去我的github