hello:大家好我是 小小小小小鹿,一枚菜鸡Android程序猿。最近正在阅读Glide源码,今日咱们要研究的部分是Glide RequestManager 生命周期办理。 本来这个也是这篇文章应该是Glide生命周期办理。可是在源码阅读中我发现本来我曾经的项目关于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生命生命周期首要分为两个:

  1. activity/fragment 生命周期办法调用,影响到整个页面一切恳求。
  2. 网络状况改变引起整个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传递的参数有下面几类。

  1. Context 会测验将其转换成对应的activity不然获取的是Application 等级的RequestManager
  2. Activity/fragment RequestManagerRetriever会测验经过他们的FragmentManager获取一个不行见的子fragment,假如没有获取成功则新建一个。并增加到activity/fragment中。
  3. 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);
   }
}

试验结果:

看完这篇,你确定你的Glide不会发生内存泄漏吗?

能够看这儿由于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 能够分为三个等级:

  1. 应用级 能够进行全局装备
  2. 页面等级 activty/fragment 能够为每一个特别的页面进行定制化处理,作用于RequestManager
  3. 单个恳求 作用于RequestBuilder 为每一个恳求构建恳求装备项

Glide怎么保证图片的加载不会出现紊乱

ViewTarget#setRequest会调用View的setTag 将request恳求目标放在View中。在恳求的时分会经过ViewTarget#getRequest,假如回来的与前一个恳求共同则运用本来的恳求,不然铲除本来的恳求。

关于运用application加载和在子线程进行图片加载,需求谨慎运用,除非你明确他们的运用场景与本身的业务符合。