前言
- 圖片模塊是 App 中非常重要的一個組件,而 Glide 作爲官方和業界雙重認可的解決方案,其學習價值不必多言;
- 在這篇文章裏,我將分析 Glide 生命週期管理,主要分爲三個層次的生命週期:Activity & 網絡 & 內存。如果能幫上忙,請務必點贊加關注,這真的對我非常重要。
提示: 本文源碼基於
Glide 4.11
目錄
1. 概述
使用 Glide 加載圖片非常簡單,類似這樣:
Glide.with(activity)
.load(url)
.into(imageView)
相對地,取消加載也很簡單,類似這樣:
Glide.with(activity).clear(imageView)
一般認爲,應該及時取消不必要的加載請求,但這並不是必須的操作。因爲 Glide 會在頁面生命週期 / 網絡變化時,自動取消加載或重新加載。
- 頁面生命週期
當頁面不可見時暫停請求;頁面可見時恢復請求;頁面銷燬時銷燬請求。在 第 2 節,我將詳細分析 Glide 的生命週期模塊,主要包括了 Activity / Fragment 兩個主體。
- 網絡連接狀態
如果從 URL 加載圖片,Glide 會監聽設備的連接狀態,並在重新連接到網絡時重啓之前失敗的請求。在 第 3 節,我將詳細分析 Glide 的網絡連接狀態監聽模塊
- 內存狀態
Glide 會監聽內存狀態,並根據不同的 level 來釋放內存。在 第 4 節,我將詳細分析 Glide 的內存狀態監聽模塊
2. Activity / Fragment 生命週期監聽
2.1 爲什麼要監聽頁面生命週期?
主要基於以下兩個目的:
-
以確保優先處理前臺可見的 Activity / Fragment,提高資源利用率;
-
在有必要時釋放資源以避免在應用在後臺時被殺死,提高穩定性;
提示: Low Memory Killer 會在合適的時機殺死進程,殺死優先級爲:空進程->後臺進程->服務進程->可見進程->前臺進程。
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);
}
此處省略參數爲 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
,而真正創建的地方在RequestManagerRetriever#get(...)
中。
先說結論,根據傳入的參數不同,將對應於 Application & Activity & Fragment 的作用域,具體如下:
線程 | 參數 | 作用域 |
---|---|---|
子線程 | / | 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 {
見下文 ...
}
}
上面的代碼已經非常簡化了,主要關注以下幾點:
1、Application 域對應的是 applicationManager,它是與RequestManagerRetriever 對象綁定的;
2、在子線程調用get(...)
,或者傳入參數是 ApplicationContext & ServiceContext 時,對應的請求是 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,之後調用supportFragmentGet(...)
獲得 RequestManager。
提示:Activity 分支與 FragmentActivity 分支類似,我不重複分析了。
- 3、Fragment 作用域
RequestManagerRetriever.java
已簡化,並略去子線程的分支
public RequestManager get(Fragment fragment) {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
}
可以看到,這裏先獲得了 Fragment 的 FragmentManager(getChildFragmentManager())
,之後調用supportFragmentGet(...)
獲得 RequestManager。
2.3 生命週期綁定
從上一節的分析知道,Activity 域和 Fragment 域都會調用supportFragmentGet(...)
來獲得 RequestManager,這一節我們專門分析這個方法:
RequestManagerRetriever.java
已簡化(提示:這個方法必在主線程執行)
用於臨時記錄 FragmentManager - SupportRequestManagerFragment 的映射關係
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 設置 Fragment 對應的 RequestMananger
current.setRequestManager(requestManager);
}
return requestManager;
}
-> 1、從 FragmentManager 中獲取 SupportRequestManagerFragment
private SupportRequestManagerFragment getSupportRequestManagerFragment(FragmentManager fm, Fragment parentHint, boolean isParentVisible) {
1.1 嘗試獲取 FRAGMENT_TAG 對應的 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 創建對象
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
1.3.2 如果父層可見,則調用 onStart() 生命週期
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
1.3.3 臨時記錄映射關係
pendingSupportRequestManagerFragments.put(fm, current);
1.3.4 提交 Fragment 事務
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 移除臨時記錄中的映射關係
FragmentManager supportFm = (FragmentManager) message.obj;
key = supportFm;
removed = pendingSupportRequestManagerFragments.remove(supportFm);
break;
上面的代碼已經非常簡化了,主要關注以下幾點:
- 1、從 FragmentManager 中獲取 SupportRequestManagerFragment;
- 2、從該 Fragment 中獲取 RequestManager;
- 3、首次獲取,則實例化 RequestManager,後續從同一個 SupportRequestManagerFragment 中都獲取的是這個 RequestManager。
其中,獲取 SupportRequestManagerFragment 的方法更爲關鍵:
- 1.1 嘗試獲取
FRAGMENT_TAG
對應的 Fragment - 1.2 嘗試從臨時記錄中獲取 Fragment
- 1.3 實例化 Fragment
- 1.3.1 創建對象
- 1.3.2 如果父層可見,則調用 onStart() 生命週期
- 1.3.3 臨時記錄映射關係
- 1.3.4 提交 Fragment 事務
- 1.3.5 post 一個消息
- 1.3.6 移除臨時記錄中的映射關係
其實一步步看下來,邏輯上不算複雜了,只有 “臨時記錄” 比較考驗源碼框架理解度。即:在提交 Fragment 事務之前,爲什麼需要先保存記錄?
這是 爲了避免 SupportRequestManagerFragment 在一個作用域中重複創建。 因爲commitAllowingStateLoss()
是將事務 post 到消息隊列中的,也就是說,事務是異步處理的,而不是同步處理的。假設沒有臨時保存記錄,那麼在事務異步等待執行時,如果調用了Glide.with(...)
,那麼就會在該作用域中重複創建 Fragment。
提示: 需要注意的是,異步不一定需要多線程,異步往往伴隨着併發,但不是必須的,近期我將會分享一篇 Kotlin 協程的文章,會具體談到這個觀點,請關注!
2.4 生命週期監聽
從上面的分析我們得知,Glide 爲每個Activity 和 Fragment 作用域創建了一個無界面的 Fragment,這一節我們來分析 Glide 如何監聽這個無界面 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);
}
默認 RequestManager 工廠接口實現類
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;
添加監聽
lifecycle.addListener(this);
}
@Override
public synchronized void onDestroy() {
...
移除監聽
lifecycle.removeListener(this);
}
可以看到,實例化 RequestManager 時需要一個 com.bumptech.glide.manager.Lifecycle
對象,這個對象是在無界面 Fragment 中創建的。當 Fragment 的生命週期變化時,就是通過這個 Lifecycle 對象將事件分發到 RequestManager。
ActivityFragmentLifecycle.java
class ActivityFragmentLifecycle implements Lifecycle {
private final Set<LifecycleListener> lifecycleListeners =
Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
...
}
2.5 生命週期回調
現在我們來看 RequestManager 收到生命週期回調後的處理。
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() 時暫停任務(頁面不可見)
pauseRequests();
targetTracker.onStop();
}
@Override
public synchronized void onStart() {
2、onStart() 時恢復任務(頁面可見)
resumeRequests();
targetTracker.onStart();
}
@Override
public synchronized void onDestroy() {
3、onDestroy() 時銷燬任務(頁面銷燬)
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();
}
}
主要關注以下幾點:
- 1、頁面不可見時暫停請求
(onStop() )
- 2、頁面可見時恢復請求
(onStart() )
- 3、頁面銷燬時銷燬請求
(onDestroy() )
3. 網絡連接狀態監聽
Glide 會監聽網絡連接狀態,並在網絡重新連接時重新啓動失敗的請求。具體分析如下:
3.1 廣播監聽器
RequestManager.java
private final ConnectivityMonitor connectivityMonitor;
RequestManager(...){
...
監聽器
connectivityMonitor = factory.build(context.getApplicationContext(), new RequestManagerConnectivityListener(requestTracker));
...
}
可以看到,在 RequestManager 的構造器中,會構建一個ConnectivityMonitor
對象。其中,默認的構建工廠是:
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、檢查是否授予監控網絡狀態的權限
int permissionResult = ContextCompat.checkSelfPermission(context, NETWORK_PERMISSION);
boolean hasPermission = permissionResult == PackageManager.PERMISSION_GRANTED;
2、實例化不同的 ConnectivityMonitor
return hasPermission
? new DefaultConnectivityMonitor(context, listener)
: new NullConnectivityMonitor();
}
}
如果有網絡監聽權限,則實例化DefaultConnectivityMonitor
:
已簡化
final class DefaultConnectivityMonitor implements ConnectivityMonitor {
private final Context context;
final ConnectivityListener listener;
boolean isConnected;
private boolean isRegistered;
1、廣播監聽器
private final BroadcastReceiver connectivityReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
boolean wasConnected = isConnected;
isConnected = isConnected(context);
5、狀態變化,回調
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、註冊廣播監聽器
isConnected = isConnected(context);
context.registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
isRegistered = true;
}
private void unregister() {
if (!isRegistered) {
return;
}
3、註銷廣播監聽器
context.unregisterReceiver(connectivityReceiver);
isRegistered = false;
}
4、檢查網絡連通性
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.
}
}
可以看到,在頁面可見時註冊廣播監聽器,而在頁面不可見時註銷廣播監聽器。
3.2 網絡連接變化回調
現在我們看 RequestManager 中是如何處理網絡連接狀態變化的。
RequestManager.java
private class RequestManagerConnectivityListener
implements ConnectivityMonitor.ConnectivityListener {
private final RequestTracker requestTracker;
RequestManagerConnectivityListener(RequestTracker requestTracker) {
this.requestTracker = requestTracker;
}
@Override
public void onConnectivityChanged(boolean isConnected) {
網絡連接,重新開啓請求
if (isConnected) {
synchronized (RequestManager.this) {
requestTracker.restartRequests();
}
}
}
}
小結:如果應用有監控網絡狀態的權限,那麼 Glide 會監聽網絡連接狀態,並在網絡重新連接時重新啓動失敗的請求。
4. 內存狀態監聽
這一節我們來分析 Glide 的內存狀態監聽模塊,具體分析如下:
Glide.java
private static void initializeGlide(...) {
...
applicationContext.registerComponentCallbacks(glide);
}
1、內存緊張級別
@Override
public void onTrimMemory(int level) {
trimMemory(level);
}
2、低內存狀態
@Override
public void onLowMemory() {
clearMemory();
}
public void trimMemory(int level) {
1.1 確保是主線程
Util.assertMainThread();
1.2 每個 RequestManager 處理內存緊張級別
for (RequestManager manager : managers) {
manager.onTrimMemory(level);
}
1.3 內存緩存處理內存緊張級別
memoryCache.trimMemory(level);
bitmapPool.trimMemory(level);
arrayPool.trimMemory(level);
}
public void clearMemory() {
1.2 確保是主線程
Util.assertMainThread();
1.2 內存緩存處理低內存狀態
memoryCache.clearMemory();
bitmapPool.clearMemory();
arrayPool.clearMemory();
}
RequestManager.java
@Override
public void onTrimMemory(int level) {
if (level == TRIM_MEMORY_MODERATE && pauseAllRequestsOnTrimMemoryModerate) {
暫停請求
pauseAllRequestsRecursive();
}
}
小結:在構建 Glide 時,會調用registerComponentCallbacks()
進行全局註冊, 系統在內存緊張的時候回調onTrimMemory()
。而 Glide 則根據系統內存緊張級別(level)進行 memoryCache / bitmapPool / arrayPool 的回收,而 RequestManager 在 TRIM_MEMORY_MODERATE
級別會暫停請求。
5. 總結
- 頁面生命週期
當頁面不可見時暫停請求;頁面可見時恢復請求;頁面銷燬時銷燬請求。
- 網絡連接狀態
如果應用有監控網絡狀態的權限,那麼 Glide 會監聽網絡連接狀態,並在網絡重新連接時重新啓動失敗的請求。
- 內存狀態
Glide 則根據系統內存緊張級別(level)進行 memoryCache / bitmapPool / arrayPool 的回收,而 RequestManager 在 TRIM_MEMORY_MODERATE
級別會暫停請求。