最近正在閱讀Glide原始碼,今天我們要研究的部分是Glide RequestManager 生命周期管理, 本來這個也是這篇文章應該是Glide生命周期管理,但是在原始碼閱讀中我發現原來我以前的專案對于Glide的使用存在著一些記憶體泄漏的可能,因此臨時決定更改了文章的名字,希望能夠引起大家的重視,
這個是我們的主界面樣式

通過最下面的一排選項卡,控制主界面的一級fragment ,一級Fragment下面又有若干的子Fragment,fragment又包含一些其它的View,以RecyclerView舉例,在對應的Adapter創建的時候會傳遞Context物件,加載的時候
Glide.with(context).load("path").into(imagerView) 這樣做會存在記憶體泄漏的可能,
下面正式分析內因為Glide使用不當造成記憶體泄漏的原理,
Glide生命周期
作為一個android開發者,說到生命周期,最先想到的應該是activity的生命周期了吧,activity的生命周期是android系統開發者給我們設定的一些模板方法,我們只需要在對應的方法中實作對應的業務邏輯即可,那么Glide的生命周期是怎么來的呢?
Glide生命生命周期主要分為兩個:
- activity/fragment 生命周期方法呼叫,影響到整個頁面所有請求,
- 網路狀態變化引起整個requestManager 所管理的所有請求發生改變,
頁面管理
Glide#with方法回傳的是一個RequestManager物件,而RequestManager的獲取實際上都呼叫了RequestManagerRetriever#get來獲取RequestManager物件的,
RequestManagerRetriever用于創建新的 RequestManager 或從Activity和Fragment中檢索現有的,
RequestManagerRetriever的構建
public RequestManagerRetriever(@Nullable RequestManagerFactory factory) {
this.factory = factory != null ? factory : DEFAULT_FACTORY;
handler = new Handler(Looper.getMainLooper(), this /* Callback */);
}
它的factory由Glide傳遞過來,如果我們不進行配置默認為空,就是使用DEFAULT_FACTORY進行創建
private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
@NonNull
@Override
public RequestManager build(@NonNull Glide glide, @NonNull Lifecycle lifecycle,
@NonNull RequestManagerTreeNode requestManagerTreeNode, @NonNull Context context) {
return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
}
};
RequestManagerRetriever獲取對應的RequestManager
RequestManagerRetriever#get傳遞的引數有下面幾類,
- Context 會嘗試將其轉換成對應的activity否則獲取的是Application 級別的RequestManager
- Activity/fragment RequestManagerRetriever會嘗試通過他們的FragmentManager獲取一個不可見的子fragment,如果沒有獲取成功則新建一個,并添加到activity/fragment中,
- View 當傳遞一個View進來的時候,會先獲取對應的activity,如果獲取不到則直接使用Application級別的RequestManager,如果獲取到了activity,會查看當前View是否在某一個activity中,如果在使用fragment獲取對應的ReauestManager 如果不在則使用Activity的RequestManager,
需要特別注意的是:不論傳遞什么引數,在子執行緒進行圖片加載都會統一使用Application級別的RequestManager,
這里以RequestManagerRetriever#get(View view)來說明其流程
@NonNull
public RequestManager get(@NonNull View view) {
//如果在子執行緒,使用Application級別的RequestManager
if (Util.isOnBackgroundThread()) {
return get(view.getContext().getApplicationContext());
}
//進行非空判斷
Preconditions.checkNotNull(view);
Preconditions.checkNotNull(view.getContext(),
"Unable to obtain a request manager for a view without a Context");
//查找對應的Activity
Activity activity = findActivity(view.getContext());
//如果activity為空則直接使用
if (activity == null) {
return get(view.getContext().getApplicationContext());
}
?
//查找到view所屬的fragment,則使用fragment 查找不到則使用activity,
if (activity instanceof FragmentActivity) {
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get(activity);
}
?
// Standard Fragments.
android.app.Fragment fragment = findFragment(view, activity);
if (fragment == null) {
return get(activity);
}
return get(fragment);
}
通過activity查找當前View所屬的fragment
private android.app.Fragment findFragment(@NonNull View target, @NonNull Activity activity) {
//是以View作為key Fragment作為value的ArrayMap
tempViewToFragment.clear();
//將所有的fragment 包含activity下的fragment和fragment中的子Fragment
//通過遞回的方式全部添加到 tempViewToFragment
findAllFragmentsWithViews(activity.getFragmentManager(), tempViewToFragment);
?
android.app.Fragment result = null;
?
View activityRoot = activity.findViewById(android.R.id.content);
View current = target;
//不斷對比直到當前的view為contentView 則停止查找fragment
while (!current.equals(activityRoot)) {
result = tempViewToFragment.get(current);
//查找到了對應的fragment,退出當前回圈
if (result != null) {
break;
}
if (current.getParent() instanceof View) {
current = (View) current.getParent();
} else {
break;
}
}
tempViewToFragment.clear();
return result;
}
通過fragment獲取RequestManager
RequestManagerRetriever#get(Fragment fragment) 會呼叫supportFragmentGet來獲取RequestManager,
@NonNull
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
//獲取當前FragmentManager 下的SupportRequestManagerFragment 在getSupportRequestManagerFragment內部,如果沒有對應的fragment,會為其添加,
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
//當前SupportRequestManagerFragment 沒有RequestManager 則創建一個RequestManager與其生命周期系結,
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
RequestManager的構建程序
RequestManager有兩個構造方法,但是最終都會執行下面這個,
RequestManager(
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode treeNode,
RequestTracker requestTracker,
ConnectivityMonitorFactory factory,
Context context) {
this.glide = glide;
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.context = context;
//創建一個網咯變化的監聽 網路監聽是Glide默認實作的,我們也可以通過指定factory實作其它的一些業務邏輯,
//當網路連接上后會將所有請求失敗的重新嘗試,
connectivityMonitor =
factory.build(
context.getApplicationContext(),
new RequestManagerConnectivityListener(requestTracker));
?
//實作
if (Util.isOnBackgroundThread()) {
mainHandler.post(addSelfToLifecycle);
} else {
lifecycle.addListener(this);
}
//將網路變化的監聽與生命周期進行系結
lifecycle.addListener(connectivityMonitor);
?
defaultRequestListeners =
new CopyOnWriteArrayList<>(glide.getGlideContext().getDefaultRequestListeners());
setRequestOptions(glide.getGlideContext().getDefaultRequestOptions());
//將當前RequestManager添加到Glide方便統一進行管理
glide.registerRequestManager(this);
}
網路監聽
在構造方法中,創建connectivityMonitor的時候,將requestTracker傳遞給了RequestManagerConnectivityListener,他的實作如下:
private class RequestManagerConnectivityListener
implements ConnectivityMonitor.ConnectivityListener {
@GuardedBy("RequestManager.this")
private final RequestTracker requestTracker;
?
RequestManagerConnectivityListener(@NonNull RequestTracker requestTracker) {
this.requestTracker = requestTracker;
}
?
@Override
public void onConnectivityChanged(boolean isConnected) {
if (isConnected) {//網路連接上會重新開始請求,
synchronized (RequestManager.this) {
requestTracker.restartRequests();
}
}
}
}
RequestManager#onDestory
@Override
public synchronized void onDestroy() {
//通知所有target呼叫onDestory
targetTracker.onDestroy();
//通知每一個target 進行清除
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
//清除集合
targetTracker.clear();
//清除當前requestManager管理的request
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
mainHandler.removeCallbacks(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
target的清除程序
RequestManger的clear(Target<?> target)會內用untrackOrDelegate
private void untrackOrDelegate(@NonNull Target<?> target) {
//清除對應的request,并對request進行重置,放進物件重用池,
boolean isOwnedByUs = untrack(target);
//如果當前的target不歸自己管理,會遍歷所有的requestManager查找到合適的requestManager進行處理,
if (!isOwnedByUs && !glide.removeFromManagers(target) && target.getRequest() != null) {
Request request = target.getRequest();
target.setRequest(null);
request.clear();
}
}
request清除程序
synchronized boolean untrack(@NonNull Target<?> target) {
Request request = target.getRequest();
// If the Target doesn't have a request, it's already been cleared.
if (request == null) {
return true;
}
?
if (requestTracker.clearRemoveAndRecycle(request)) {
//從對應的集合中移除
targetTracker.untrack(target);
target.setRequest(null);
return true;
} else {
return false;
}
}
?
public boolean clearRemoveAndRecycle(@Nullable Request request) {
return clearRemoveAndMaybeRecycle(request, /*isSafeToRecycle=*/ true);
}
?
private boolean clearRemoveAndMaybeRecycle(@Nullable Request request, boolean isSafeToRecycle) {
if (request == null) {
return true;
}
//如果能夠從集合中移除成功,那么這個request歸屬當前的RequestManager管理
boolean isOwnedByUs = requests.remove(request);
// Avoid short circuiting.
isOwnedByUs = pendingRequests.remove(request) || isOwnedByUs;
if (isOwnedByUs) {
//執行request.clear 對request狀態進行轉變,和做相應的通知
request.clear();
if (isSafeToRecycle) {
//這個是不會進行記憶體泄漏的關鍵,將request對應的參考回呼置空,切斷參考關系,
//對應代碼可以參考SingleRequest
request.recycle();
}
}
return isOwnedByUs;
}
Glide真的不會發生記憶體泄漏嗎?
前面我們梳理了Glide的生命周期,知道在生命相關的activity/Fragment銷毀的時候會暫停和回收相關的請求,并且切斷網路請求回呼的參考,那么Glide是不是真的能夠完全避免記憶體內泄漏呢?
這個直接給出我的結論:正常情況下使用Glide不會造成內Activity、Fragment、View記憶體泄漏,但是如果Glide使用不當是可能造成記憶體泄漏的,比如在Fragment使用Glide#with傳遞activity物件, 原因是Fragment結束的時候,Activity幾倍RequestManager并沒有接收到相應的生命周期方法,
實驗證明:
改造我們在Glide資料輸入輸出撰寫的加載音頻封面的ModelLoader,當遇到特定該音頻的時候執行緒休眠300秒
@Override
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
try {
Log.d(TAG,"loadData assetPath "+assetPath);
AssetFileDescriptor fileDescriptor = assetManager.openFd(assetPath);
//特定路徑 休眠300s 模擬網路加載緩慢
if(assetPath.contains("DuiMianDeNvHaiKanGuoLai--RenXianQi.mp3")){
SystemClock.sleep(10*30*1000);//休眠300s
}
mediaMetadataRetriever.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(),fileDescriptor.getDeclaredLength());
byte[] bytes = mediaMetadataRetriever.getEmbeddedPicture();
if(bytes == null){
callback.onLoadFailed(new FileNotFoundException("the file not pic"));
return;
}
ByteBuffer buf = ByteBuffer.wrap(bytes);
Log.d(TAG,"loadData assetPath "+assetPath +" success");
callback.onDataReady(buf);
} catch (IOException e) {
e.printStackTrace();
callback.onLoadFailed(e);
}
}
在Activity中添加一個Fragment,當頁面創建成功后,使用Glide#with傳遞activity/context物件,并在activity中移除該Fragment,
將fragment的根View與fragment強制關聯,方便利用LeakCanary進行記憶體泄漏檢測,
public static class MyTestFragment extends Fragment {
ImageView imageView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_glide_source_test,container,false);
imageView = root.findViewById(R.id.imageView);
//強制保留參考關系 方便進行檢測
root.setTag(this);
Log.d(TAG,"onCreateView finish");
return root;
}
?
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
?
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Glide.with(getActivity()).load(Uri.parse("file:///android_asset/DuiMianDeNvHaiKanGuoLai--RenXianQi.mp3")).diskCacheStrategy(DiskCacheStrategy.NONE).into(imageView);
Log.d(TAG,"onActivityCreated load ");
imageView.postDelayed(new Runnable() {
@Override
public void run() {
Log.d(TAG,"onActivityCreated remove ");
getActivity().getSupportFragmentManager().beginTransaction().remove(MyTestFragment.this).commit();
}
},300);
?
}
?
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
實驗結果:

可以看這里因為Fragment被View持有導致了Fragment記憶體泄漏,這個也就反應了當Glide使用不當,會導致View的記憶體泄漏, 解決:傳遞正確的引數給with,或者呼叫ViewTarget#clearOnDetach,我沒有使用過clearOnDetach 根據Glide注釋,這個是一組實驗性api,后續可能會被移除,
小結Glide使用注意事項
Glide#with方法在引數使用優先級
fragment > view > activity > application
其中view和activity 在明確知道當前使用的頁面是activity優先傳遞activity 因為view會通過多次回圈遍歷查找fragment、activity,正確的使用Glide可以避免因為Glide造成記憶體泄漏,
Glide RequestOptions 可以分為三個級別:
- 應用級 可以進行全域配置
- 頁面級別 activty/fragment 可以為每一個特殊的頁面進行定制化處理,作用于RequestManager
- 單個請求 作用于RequestBuilder 為每一個請求構建請求配置項
Glide如何保證圖片的加載不會出現錯亂
ViewTarget#setRequest會呼叫View的setTag 將request請求物件放在View中,在請求的時候會通過ViewTarget#getRequest,如果回傳的與前一個請求一致則使用原來的請求,否則清除原來的請求,
對于使用application加載和在子執行緒進行圖片加載,需要謹慎使用,除非你明確他們的使用場景與自身的業務契合,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/374829.html
標籤:其他
