内存泄漏这个锅Glide不背——聊聊Glide到底把context怎么了

内存泄漏这个锅Glide不背——聊聊Glide到底把context怎么了

Android小彩虹2021-07-16 4:04:44100A+A-

前言

今天刷到一篇内容和观点都挺新颖的文章《小题大做 | Handler内存泄露全面分析》,看到后面如何解决内存泄漏时有一段内容引起我的思考,

“比如Glide使用的时候传的上下文不要用Activity而改用Application的上下文。”

印象中这个说法中在很多其他文章都提到过,矛头都指向了“万恶的”Glide.with(context),我想Glide这个量级的开源项目了,会犯这种低级错误吗,它到底拿这个context去干了什么?

开扒源码

就拿Glide的Master代码来分析,快进到最危险的Glide.with(Activity activity)方法,传入参数activity,

  public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }

getRetreiver方法是一个获取RequestManager的获取器,

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
             …
       return Glide.get(context).getRequestManagerRetriever();
  }

传给Glide.get,初始化一个Glide的单例,

 public static Glide get(@NonNull Context context) {
    if (glide == null) {
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }
    return glide;
  }

其中getAnnotationGeneratedGlideModules传入的是applicationContext,这里不存在activity泄漏,继续看getAnnotationGeneratedGlideModules->initializeGlide,

private final GlideContext glideContext;
…
private static void initializeGlide( @NonNull Context context, @NonNull GlideBuilder builder, @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
    Context applicationContext = context.getApplicationContext();
    ...

可以看到第一行就是获取applicationContext,后面的代码都只使用了applicationContet,这个context被保存到GlideContext里,glideContext是单例Glide的成员,所以这里也排除了activity泄漏的可能。

回到开头,难道是RequestManagerRetriver.get(Activity)方法有问题?

public RequestManager get(@NonNull Context context) {
    if (context == null) {
      throw new IllegalArgumentException("You cannot start a load on a null Context");
    } else if (Util.isOnMainThread() && !(context instanceof Application)) {
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper
          // Only unwrap a ContextWrapper if the baseContext has a non-null application context.
          // Context#createPackageContext may return a Context without an Application instance,
          // in which case a ContextWrapper may be used to attach one.
          && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }

    return getApplicationManager(context);
  }

跟进get((Activity) context)

 public RequestManager get(@NonNull Activity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else if (activity instanceof FragmentActivity) {
      return get((FragmentActivity) activity);
    } else {
      assertNotDestroyed(activity);
      frameWaiter.registerSelf(activity);
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }
  

第一个情况在后台线程getApplication没问题,其他分支就是通过Activity获取fragmentManager,Glide通过空白Fragment监听Activity生命周期来调度请求的方案,这里就不做细究了,我们只关心activity最后流向了哪里可能会导致泄漏的。

跟进所有分支,发现获取fragmentManager后,activity并没有被存储起来,除了有的会走到Glide的单例构造方法,但传入的也是applicationContext,例如:

private RequestManager getApplicationManager(@NonNull Context context) {
    // Either an application context or we're on a background thread.
    if (applicationManager == null) {
      synchronized (this) {
          ...
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager =
              factory.build(
                  glide,
                  new ApplicationLifecycle(),
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }

    return applicationManager;
  }

得出结论

Glide拿context主要就两个事情:

  1. 用applicationContext构造glideContext,获取上下文信息;
  2. 获取FragmentManager监听activity生命周期

所以不管你在Glide.with(context)传入的是什么context,理论上都不会造成这个context的泄漏,除了FragmentManager可能存在bug,或是RequestManagerFragment出了bug导致内存问题,Glide本身的设计上是不存在大问题的。毕竟是被这么多人使用久经考验的工具库。

当然也许旧版本的Glide可能存在泄漏的问题今天就不去考究了,至少目前最新release的版本,大家可以放心的给Glide传入activity!

在RePlugin插件里使用Glide的一些问题

RePlugin框架的插件中使用Glide,传入哪个context还是要注意一下的。

Glide的placeholder在加载的时候,如果传入的是资源id,会去上文提到的glideContext里的context去加载drawable,

  private Drawable loadDrawable(@DrawableRes int resourceId) {
    Theme theme =
        requestOptions.getTheme() != null ? requestOptions.getTheme() : context.getTheme();
    return DrawableDecoderCompat.getDrawable(glideContext, resourceId, theme);
  }

因为Glide是单例构造,如果在插件中第一次使用传入的context不是插件的context的话,会导致后续加载drawble的时候出现异常。

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1

联系我们