【问题标题】:Glide image loading with application context使用应用程序上下文滑动图像加载
【发布时间】:2022-03-22 21:14:06
【问题描述】:

我在我的 android 应用程序中使用 glide 进行图像加载,以避免任何崩溃,我正在使用应用程序上下文加载图像。这会对应用程序和内存的性能产生什么影响?

【问题讨论】:

    标签: android performance android-glide


    【解决方案1】:

    这会对应用程序和内存的性能产生什么影响?

    Glide 提供这么多.with() 方法是有原因的:它遵循生命周期。

    想象一个动态添加到活动中的Fragment。在其onCreateView 方法中,它启动了一个 3MB 图像的 Glide 加载。现在,如果用户按下后退按钮并且 Fragment 被移除或整个 Activity 被关闭怎么办?

    • 如果你使用with(getActivity().getApplicationContext()),什么都不会发生,所有 3MB 的数据都会被下载,然后解码,缓存,甚至可能设置到 ImageView,然后被垃圾收集,因为唯一的引用来自 Glide 内部。
    • 如果你使用with((Fragment)this)Glide 订阅Fragment 的生命周期事件,并且一旦Fragment 停止,任何未完成的请求都应该暂停;当被销毁时,所有待处理的请求都会被清除。这意味着图像下载将中途停止,该死片段将不再使用任何资源。
    • 如果您使用with(getActivity()) Glide 订阅 Activity 的生命周期事件,并且发生与上述相同的事情,但仅在 Activity 停止或销毁时发生。

    因此,最佳做法是使用最接近的上下文/片段来避免未使用的请求完成! (还有一种手动停止加载的方法:Glide.clear(ImageView|Target)。)


    要在实践中应用这一点,请尽可能使用with(this),但如果不是,例如在适配器或集中式图像加载方法中,请传入RequestManager glide作为参数并使用glide.load(...,用于示例:

    static loadImage(RequestManager glide, String url, ImageView view) {
        glide.load(url).into(view);
    }
    

    或在适配器中:

    class MyAdapter extends WhichEveryOneYouUse {
        private final RequestManager glide;
        MyAdapter(RequestManager glide, ...) {
            this.glide = glide;
            ...
        }
        void getView/onBindViewHolder(... int position) {
            // ... holder magic, and get current item for position
            glide.load... or even loadImage(glide, item.url, holder.image);
        }
    }
    

    并从 Activity/Fragment 中使用这些:

    loadImage(Glide.with(this), url, findViewById(R.id.image));
    // or
    list.setAdapter(new MyAdapter(Glide.with(this), data));
    

    【讨论】:

    • 很棒的解释!它节省了我很多时间来调查经常出现 OOM 异常的主要原因是什么。谢谢!
    • 注意:对于出现在该活动上的任何视图,调用Glide.with(view.getContext()) 实际上等效于Glide.with(this),因为RequestManagerRetriever.get(Context context) 检查上下文是否是Activity 的实例并适当地转换它,例如get((Activity)context)。因此,无论哪种方式,它最终都会使用相同的 get(Activity) 方法。
    • 所以我们不需要手动调用Glide.with(this).onDestroy()?假设我们在Glide.with().. 调用中使用正确的Context,因为Glide 将挂钩到Activity/Fragments 生命周期?
    • 直到 Glide 开发者修复“You cannot start a load for a destroy Activity”抛出异常,我建议你使用 ApplicationContext
    • @Nurseyit Coil 的作者在这里。与 Glide 类似,如果您在 Fragment 内启动 load,Coil 将使用 Activity 的生命周期。但是,Coil 和 Glide 都会响应 View.onDetach 事件,这些事件在 Fragment 被移到 backstack 时触发。此外,由于 Coil 使用 AndroidX 生命周期组件,因此在已销毁的 Activity 内部发出的任何请求都将立即取消。
    【解决方案2】:

    将 Glide 请求与所有者的生命周期同步的通用解决方案。可以从任何地方调用:Activity、Fragment、RV Adapter、自定义视图等。

    private fun RequestManager.syncWithLifecycleOwner(view: View): RequestManager {
    
    val syncRequest = object : DefaultLifecycleObserver {
        override fun onStart(owner: LifecycleOwner) = onStart()
        override fun onStop(owner: LifecycleOwner) = onStop()
        override fun onDestroy(owner: LifecycleOwner) {
            onDestroy()
            owner.lifecycle.removeObserver(this)
        }
    }
    
    view.findViewTreeLifecycleOwner()?.lifecycle?.addObserver(syncRequest)
    
    return this
    

    }

    然后你可以像这样制作一个简单的扩展函数:

    fun ImageView.loadUrl(url: String) {
       Glide
          .with(context.applicationContext)
          .syncWithLifecycleOwner(this)
          .load(url)
          .into(this) 
    }
    

    findViewTreeLifecycleOwner() 存在于 AndroidX 生命周期库中。它提供了这个特定 ImageView 附加到的 Activity 或 Fragment View 的生命周期 (viewLifecycleOwner)。您需要从视图中传递应用程序上下文,以确保 Glide 库本身不会调用回调。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-01
      • 1970-01-01
      • 2011-05-13
      • 2016-07-06
      • 2012-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多