Android | 適可而止!看Glide如何把生命周期安排得明明白白

前言

  • 圖片模塊是 App 中非常重要的一個組件,而 Glide 作為官方和業(yè)界雙重認(rèn)可的解決方案,其學(xué)習(xí)價值不必多言;
  • 在這篇文章里,我將分析 Glide 生命周期管理,主要分為三個層次的生命周期:Activity & 網(wǎng)絡(luò) & 內(nèi)存。如果能幫上忙,請務(wù)必點贊加關(guān)注,這真的對我非常重要。

相關(guān)文章

提示: 本文源碼基于Glide 4.11


目錄


1. 概述

使用 Glide 加載圖片非常簡單,類似這樣:

Glide.with(activity)
    .load(url)
    .into(imageView)

相對地,取消加載也很簡單,類似這樣:

Glide.with(activity).clear(imageView)

一般認(rèn)為,應(yīng)該及時取消不必要的加載請求,但這并不是必須的操作。因為 Glide 會在頁面生命周期 / 網(wǎng)絡(luò)變化時,自動取消加載或重新加載。

  • 頁面生命周期

當(dāng)頁面不可見時暫停請求;頁面可見時恢復(fù)請求;頁面銷毀時銷毀請求。在 第 2 節(jié),我將詳細(xì)分析 Glide 的生命周期模塊,主要包括了 Activity / Fragment 兩個主體。

  • 網(wǎng)絡(luò)連接狀態(tài)

如果從 URL 加載圖片,Glide 會監(jiān)聽設(shè)備的連接狀態(tài),并在重新連接到網(wǎng)絡(luò)時重啟之前失敗的請求。在 第 3 節(jié),我將詳細(xì)分析 Glide 的網(wǎng)絡(luò)連接狀態(tài)監(jiān)聽模塊

  • 內(nèi)存狀態(tài)

Glide 會監(jiān)聽內(nèi)存狀態(tài),并根據(jù)不同的 level 來釋放內(nèi)存。在 第 4 節(jié),我將詳細(xì)分析 Glide 的內(nèi)存狀態(tài)監(jiān)聽模塊


2. Activity / Fragment 生命周期監(jiān)聽

2.1 為什么要監(jiān)聽頁面生命周期?

主要基于以下兩個目的:

  • 以確保優(yōu)先處理前臺可見的 Activity / Fragment,提高資源利用率;

  • 在有必要時釋放資源以避免在應(yīng)用在后臺時被殺死,提高穩(wěn)定性;

提示: Low Memory Killer 會在合適的時機殺死進程,殺死優(yōu)先級為:空進程->后臺進程->服務(wù)進程->可見進程->前臺進程。

2.2 三種生命周期作用域

首先,我們從 Glide 的入口方法入手:

Glide.java

private final RequestManagerRetriever requestManagerRetriever;

入口方法:
public static RequestManager with(Context context) {
    return getRetriever(context).get(context);
}

入口方法:
public static RequestManager with(Activity activity) {
    return getRetriever(activity).get(activity);
}

此處省略參數(shù)為 FragmentActivity、Fragment、View 的類似方法...

private static RequestManagerRetriever getRetriever(Context context) {
    其中,Glide.get(context) 基于 DCL 單例
    return Glide.get(context).getRequestManagerRetriever();
}

public RequestManagerRetriever getRequestManagerRetriever() {
    return requestManagerRetriever;
}

可以看到,with(...)方法的返回值是RequestManager,而真正創(chuàng)建的地方在RequestManagerRetriever#get(...)中。

先說結(jié)論,根據(jù)傳入的參數(shù)不同,將對應(yīng)于 Application & Activity & Fragment 的作用域,具體如下:

線程 參數(shù) 作用域
子線程 / Application
主線程(下同) ApplicationContext/
ServiceContext
Application
/ FragmentActivity Activity
/ Activity Activity
/ Fragment Fragment
/ View Activity / Fragment
  • 1、Application 作用域

對于 Application 作用域的請求,它的生命周期是全局的,不與具體頁面綁定。

RequestManagerRetriever.java

已簡化

Application 域請求管理
private volatile RequestManager applicationManager;

private RequestManager getApplicationManager(@NonNull Context context) {
    源碼基于 DCL 單例
    return applicationManager;
}

public RequestManager get(@NonNull Context context) {
    if (Util.isOnMainThread() && !(context instanceof Application)) {

        2、FragmentActivity
        if (context instanceof FragmentActivity) {
            return get((FragmentActivity) context);
        } 

        3、Activity
        else if (context instanceof Activity) {
            return get((Activity) context);
        }
    }
    1、Application
    return getApplicationManager(context);
  }

public RequestManager get(@NonNull FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
        return get(activity.getApplicationContext());
    } else {
        見下文 ...
    }
}

上面的代碼已經(jīng)非常簡化了,主要關(guān)注以下幾點:

1、Application 域?qū)?yīng)的是 applicationManager,它是與RequestManagerRetriever 對象綁定的;

2、在子線程調(diào)用get(...),或者傳入?yún)?shù)是 ApplicationContext & ServiceContext 時,對應(yīng)的請求是 Application 域。

  • 2、Activity 作用域

RequestManagerRetriever.java

已簡化,并略去子線程的分支

public RequestManager get(FragmentActivity activity) {
    FragmentManager fm = activity.getSupportFragmentManager();
    return supportFragmentGet(activity, fm, null, isActivityVisible(activity));
  }

public RequestManager get(Activity activity) {
    android.app.FragmentManager fm = activity.getFragmentManager();
    return fragmentGet(activity, fm, null, isActivityVisible(activity));
}

可以看到,這里先獲得了 FragmentActivity 的 FragmentManager,之后調(diào)用supportFragmentGet(...)獲得 RequestManager。

提示:Activity 分支與 FragmentActivity 分支類似,我不重復(fù)分析了。

  • 3、Fragment 作用域

RequestManagerRetriever.java

已簡化,并略去子線程的分支

public RequestManager get(Fragment fragment) {
    FragmentManager fm = fragment.getChildFragmentManager();
    return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
}

可以看到,這里先獲得了 Fragment 的 FragmentManager(getChildFragmentManager()),之后調(diào)用supportFragmentGet(...)獲得 RequestManager。

2.3 生命周期綁定

從上一節(jié)的分析知道,Activity 域和 Fragment 域都會調(diào)用supportFragmentGet(...)來獲得 RequestManager,這一節(jié)我們專門分析這個方法:

RequestManagerRetriever.java

已簡化(提示:這個方法必在主線程執(zhí)行)

用于臨時記錄 FragmentManager - SupportRequestManagerFragment 的映射關(guān)系
final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = new HashMap<>();

private RequestManager supportFragmentGet(
      Context context,
      FragmentManager fm,
      Fragment parentHint,
      boolean isParentVisible) {
    
    1、從 FragmentManager 中獲取 SupportRequestManagerFragment
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);

    2、從該 Fragment 中獲取 RequestManager
    RequestManager requestManager = current.getRequestManager();

    3、首次獲取,則實例化 RequestManager
    if (requestManager == null) {

        3.1 實例化
        Glide glide = Glide.get(context);
        requestManager = factory.build(...);

        3.2 設(shè)置 Fragment 對應(yīng)的 RequestMananger
        current.setRequestManager(requestManager);
    }

    return requestManager;
}

->  1、從 FragmentManager 中獲取 SupportRequestManagerFragment
private SupportRequestManagerFragment getSupportRequestManagerFragment(FragmentManager fm, Fragment parentHint, boolean isParentVisible) {

    1.1 嘗試獲取 FRAGMENT_TAG 對應(yīng)的 Fragment
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);

    if (current == null) {
        1.2 嘗試從臨時記錄中獲取 Fragment
        current = pendingSupportRequestManagerFragments.get(fm);

        1.3 實例化 Fragment
        if (current == null) {
            
            1.3.1 創(chuàng)建對象
            current = new SupportRequestManagerFragment();
            current.setParentFragmentHint(parentHint);

            1.3.2 如果父層可見,則調(diào)用 onStart() 生命周期
            if (isParentVisible) {
                current.getGlideLifecycle().onStart();
            }
        
            1.3.3 臨時記錄映射關(guān)系
            pendingSupportRequestManagerFragments.put(fm, current);
          
            1.3.4 提交 Fragment 事務(wù)
            fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
            
            1.3.5 post 一個消息
            handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
        }
    }
    return current;
}

-> 1.3.5 post 一個消息
case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER:

    1.3.6 移除臨時記錄中的映射關(guān)系
    FragmentManager supportFm = (FragmentManager) message.obj;
    key = supportFm;
    removed = pendingSupportRequestManagerFragments.remove(supportFm);
break;

上面的代碼已經(jīng)非常簡化了,主要關(guān)注以下幾點:

  • 1、從 FragmentManager 中獲取 SupportRequestManagerFragment;
  • 2、從該 Fragment 中獲取 RequestManager;
  • 3、首次獲取,則實例化 RequestManager,后續(xù)從同一個 SupportRequestManagerFragment 中都獲取的是這個 RequestManager。

其中,獲取 SupportRequestManagerFragment 的方法更為關(guān)鍵:

  • 1.1 嘗試獲取FRAGMENT_TAG對應(yīng)的 Fragment
  • 1.2 嘗試從臨時記錄中獲取 Fragment
  • 1.3 實例化 Fragment
    • 1.3.1 創(chuàng)建對象
    • 1.3.2 如果父層可見,則調(diào)用 onStart() 生命周期
    • 1.3.3 臨時記錄映射關(guān)系
    • 1.3.4 提交 Fragment 事務(wù)
    • 1.3.5 post 一個消息
    • 1.3.6 移除臨時記錄中的映射關(guān)系

其實一步步看下來,邏輯上不算復(fù)雜了,只有 “臨時記錄” 比較考驗源碼框架理解度。即:在提交 Fragment 事務(wù)之前,為什么需要先保存記錄?

這是 為了避免 SupportRequestManagerFragment 在一個作用域中重復(fù)創(chuàng)建。 因為commitAllowingStateLoss()是將事務(wù) post 到消息隊列中的,也就是說,事務(wù)是異步處理的,而不是同步處理的。假設(shè)沒有臨時保存記錄,那么在事務(wù)異步等待執(zhí)行時,如果調(diào)用了Glide.with(...),那么就會在該作用域中重復(fù)創(chuàng)建 Fragment。

提示: 需要注意的是,異步不一定需要多線程,異步往往伴隨著并發(fā),但不是必須的,近期我將會分享一篇 Kotlin 協(xié)程的文章,會具體談到這個觀點,請關(guān)注!

2.4 生命周期監(jiān)聽

從上面的分析我們得知,Glide 為每個Activity 和 Fragment 作用域創(chuàng)建了一個無界面的 Fragment,這一節(jié)我們來分析 Glide 如何監(jiān)聽這個無界面 Fragment 的生命周期。

SupportRequestManagerFragment.java

private final ActivityFragmentLifecycle lifecycle;

public SupportRequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
}

@Override
public void onStart() {
    super.onStart();
    lifecycle.onStart();
}

@Override
public void onStop() {
    super.onStop();
    lifecycle.onStop();
}

@Override
public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
    unregisterFragmentWithRoot();
}

@NonNull
ActivityFragmentLifecycle getGlideLifecycle() {
    return lifecycle;
}

RequestManagerRetriever.java

-> 3.1 實例化 RequestManager
Glide glide = Glide.get(context);
requestManager = factory.build(glide, current.getGlideLifecycle(), 
    current.getRequestManagerTreeNode(), context);

RequestManager 工廠接口
public interface RequestManagerFactory {
    RequestManager build(
        Glide glide,
        Lifecycle lifecycle,
        RequestManagerTreeNode requestManagerTreeNode,
        Context context);
    }

默認(rèn) RequestManager 工廠接口實現(xiàn)類
private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
    @Override
    public RequestManager build(
        Glide glide,
        Lifecycle lifecycle,
        RequestManagerTreeNode requestManagerTreeNode,
        Context context) {
            return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
        }
    };
}

RequestManager.java

已簡化

final Lifecycle lifecycle;

RequestManager(Glide glide, Lifecycle lifecycle, ...){
    ...
    this.lifecycle = lifecycle;
    
    添加監(jiān)聽
    lifecycle.addListener(this);
}

@Override
public synchronized void onDestroy() {
    ...
    移除監(jiān)聽
    lifecycle.removeListener(this);
}

可以看到,實例化 RequestManager 時需要一個 com.bumptech.glide.manager.Lifecycle對象,這個對象是在無界面 Fragment 中創(chuàng)建的。當(dāng) Fragment 的生命周期變化時,就是通過這個 Lifecycle 對象將事件分發(fā)到 RequestManager。

ActivityFragmentLifecycle.java

class ActivityFragmentLifecycle implements Lifecycle {

    private final Set<LifecycleListener> lifecycleListeners =
      Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());

    ...
}

2.5 生命周期回調(diào)

現(xiàn)在我們來看 RequestManager 收到生命周期回調(diào)后的處理。

LifecycleListener.java

public interface LifecycleListener {
    void onStart();
    void onStop();
    void onDestroy();
}

RequestManager.java

private final RequestTracker requestTracker;

public class RequestManager
    implements ComponentCallbacks2, LifecycleListener, ... {

    @Override
    public synchronized void onStop() {

        1、onStop() 時暫停任務(wù)(頁面不可見)
        pauseRequests();
        targetTracker.onStop();
    }

    @Override
    public synchronized void onStart() {
        
        2、onStart() 時恢復(fù)任務(wù)(頁面可見)
        resumeRequests();
        targetTracker.onStart();
    }

    @Override
    public synchronized void onDestroy() {

        3、onDestroy() 時銷毀任務(wù)(頁面銷毀)
        targetTracker.onDestroy();
        for (Target<?> target : targetTracker.getAll()) {
            clear(target);
        }
        targetTracker.clear();
        requestTracker.clearRequests();
        lifecycle.removeListener(this);
        lifecycle.removeListener(connectivityMonitor);
        mainHandler.removeCallbacks(addSelfToLifecycle);
        glide.unregisterRequestManager(this);
    }
    
    public synchronized void pauseRequests() {
        requestTracker.pauseRequests();
    }

    public synchronized void resumeRequests() {
        requestTracker.resumeRequests();
    }
}

主要關(guān)注以下幾點:

  • 1、頁面不可見時暫停請求(onStop() )
  • 2、頁面可見時恢復(fù)請求(onStart() )
  • 3、頁面銷毀時銷毀請求(onDestroy() )

3. 網(wǎng)絡(luò)連接狀態(tài)監(jiān)聽

Glide 會監(jiān)聽網(wǎng)絡(luò)連接狀態(tài),并在網(wǎng)絡(luò)重新連接時重新啟動失敗的請求。具體分析如下:

3.1 廣播監(jiān)聽器

RequestManager.java

private final ConnectivityMonitor connectivityMonitor;

RequestManager(...){
    ...
    監(jiān)聽器
    connectivityMonitor = factory.build(context.getApplicationContext(), new RequestManagerConnectivityListener(requestTracker));
    ...
}

可以看到,在 RequestManager 的構(gòu)造器中,會構(gòu)建一個ConnectivityMonitor對象。其中,默認(rèn)的構(gòu)建工廠是:

DefaultConnectivityMonitorFactory.java

public class DefaultConnectivityMonitorFactory implements ConnectivityMonitorFactory {

    private static final String TAG = "ConnectivityMonitor";
    private static final String NETWORK_PERMISSION = "android.permission.ACCESS_NETWORK_STATE";

    @Override
    public ConnectivityMonitor build(Context context, ConnectivityMonitor.ConnectivityListener listener) {
      
        1、檢查是否授予監(jiān)控網(wǎng)絡(luò)狀態(tài)的權(quán)限
        int permissionResult = ContextCompat.checkSelfPermission(context, NETWORK_PERMISSION);
        boolean hasPermission = permissionResult == PackageManager.PERMISSION_GRANTED;
        
        2、實例化不同的 ConnectivityMonitor
        return hasPermission
            ? new DefaultConnectivityMonitor(context, listener)
            : new NullConnectivityMonitor();
  }
}

如果有網(wǎng)絡(luò)監(jiān)聽權(quán)限,則實例化DefaultConnectivityMonitor

已簡化
final class DefaultConnectivityMonitor implements ConnectivityMonitor {

    private final Context context;
    final ConnectivityListener listener;

    boolean isConnected;
    private boolean isRegistered;

    1、廣播監(jiān)聽器
    private final BroadcastReceiver connectivityReceiver =
        new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                boolean wasConnected = isConnected;
                isConnected = isConnected(context);

                5、狀態(tài)變化,回調(diào)
                if (wasConnected != isConnected) {
                    listener.onConnectivityChanged(isConnected);
                }
            }
        };

    DefaultConnectivityMonitor(Context context, ConnectivityListener listener) {
        this.context = context.getApplicationContext();
        this.listener = listener;
    }

    private void register() {
        if (isRegistered) {
            return;
        }

        2、注冊廣播監(jiān)聽器
        isConnected = isConnected(context);
        context.registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
        isRegistered = true;
    }

    private void unregister() {
        if (!isRegistered) {
            return;
        }
        
        3、注銷廣播監(jiān)聽器
        context.unregisterReceiver(connectivityReceiver);
        isRegistered = false;
    }

    4、檢查網(wǎng)絡(luò)連通性
    boolean isConnected(Context context) {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        return networkInfo.isConnected();
    }

    @Override
    public void onStart() {
        頁面可見時注冊
        register();
    }

    @Override
    public void onStop() {
        頁面不可見時注銷
        unregister();
    }

    @Override
    public void onDestroy() {
        // Do nothing.
    }
}

可以看到,在頁面可見時注冊廣播監(jiān)聽器,而在頁面不可見時注銷廣播監(jiān)聽器。

3.2 網(wǎng)絡(luò)連接變化回調(diào)

現(xiàn)在我們看 RequestManager 中是如何處理網(wǎng)絡(luò)連接狀態(tài)變化的。

RequestManager.java


private class RequestManagerConnectivityListener
      implements ConnectivityMonitor.ConnectivityListener {
    
    private final RequestTracker requestTracker;

    RequestManagerConnectivityListener(RequestTracker requestTracker) {
        this.requestTracker = requestTracker;
    }

    @Override
    public void onConnectivityChanged(boolean isConnected) {

        網(wǎng)絡(luò)連接,重新開啟請求
        if (isConnected) {
            synchronized (RequestManager.this) {
                requestTracker.restartRequests();
            }
        }
    }
}

小結(jié):如果應(yīng)用有監(jiān)控網(wǎng)絡(luò)狀態(tài)的權(quán)限,那么 Glide 會監(jiān)聽網(wǎng)絡(luò)連接狀態(tài),并在網(wǎng)絡(luò)重新連接時重新啟動失敗的請求。


4. 內(nèi)存狀態(tài)監(jiān)聽

這一節(jié)我們來分析 Glide 的內(nèi)存狀態(tài)監(jiān)聽模塊,具體分析如下:

Glide.java

private static void initializeGlide(...) {
    ...
    applicationContext.registerComponentCallbacks(glide);
}

1、內(nèi)存緊張級別
@Override
public void onTrimMemory(int level) {
    trimMemory(level);
}

2、低內(nèi)存狀態(tài)
@Override
public void onLowMemory() {
    clearMemory();
}

public void trimMemory(int level) {
    1.1 確保是主線程
    Util.assertMainThread();

    1.2 每個 RequestManager 處理內(nèi)存緊張級別
    for (RequestManager manager : managers) {
        manager.onTrimMemory(level);
    }
    
    1.3 內(nèi)存緩存處理內(nèi)存緊張級別
    memoryCache.trimMemory(level);
    bitmapPool.trimMemory(level);
    arrayPool.trimMemory(level);
}

public void clearMemory() {
    1.2 確保是主線程
    Util.assertMainThread();

    1.2 內(nèi)存緩存處理低內(nèi)存狀態(tài)
    memoryCache.clearMemory();
    bitmapPool.clearMemory();
    arrayPool.clearMemory();
}

RequestManager.java

@Override
public void onTrimMemory(int level) {
    if (level == TRIM_MEMORY_MODERATE && pauseAllRequestsOnTrimMemoryModerate) {
        暫停請求
        pauseAllRequestsRecursive();
    }
}

小結(jié):在構(gòu)建 Glide 時,會調(diào)用registerComponentCallbacks()進行全局注冊, 系統(tǒng)在內(nèi)存緊張的時候回調(diào)onTrimMemory()。而 Glide 則根據(jù)系統(tǒng)內(nèi)存緊張級別(level)進行 memoryCache / bitmapPool / arrayPool 的回收,而 RequestManager 在 TRIM_MEMORY_MODERATE 級別會暫停請求。


5. 總結(jié)

  • 頁面生命周期

當(dāng)頁面不可見時暫停請求;頁面可見時恢復(fù)請求;頁面銷毀時銷毀請求。

  • 網(wǎng)絡(luò)連接狀態(tài)

如果應(yīng)用有監(jiān)控網(wǎng)絡(luò)狀態(tài)的權(quán)限,那么 Glide 會監(jiān)聽網(wǎng)絡(luò)連接狀態(tài),并在網(wǎng)絡(luò)重新連接時重新啟動失敗的請求。

  • 內(nèi)存狀態(tài)

Glide 則根據(jù)系統(tǒng)內(nèi)存緊張級別(level)進行 memoryCache / bitmapPool / arrayPool 的回收,而 RequestManager 在 TRIM_MEMORY_MODERATE 級別會暫停請求。


參考資料

推薦閱讀

感謝喜歡!你的點贊是對我最大的鼓勵!歡迎關(guān)注彭旭銳的GitHub!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容