Glide 4中如何为自定义ModelLoader获取Context?
在Glide 4中为自定义ModelLoader安全获取Context的正确姿势
刚好之前在项目里折腾过Glide 4自定义ModelLoader的这个问题,踩过内存泄漏的坑,来给你唠唠正确的姿势~
首先你说的没错:绝对不能在ModelLoaderFactory里长期存储Context,不然很容易因为Factory被Glide长期持有,导致Activity这类上下文无法被回收,引发内存泄漏。那怎么把Context传给最终的ModelLoader呢?其实关键就在Factory的构造和build方法的配合上:
核心思路
在registerComponents方法里拿到Context后,把它传给Factory的构造函数,但Factory只做临时中转,不把Context存在成员变量长期持有,而是在build方法创建ModelLoader实例时,直接把Context传递给ModelLoader,同时ModelLoader内部要转存ApplicationContext来避免泄漏。
示例代码
1. 实现ModelLoaderFactory
public class RawModelLoaderFactory implements ModelLoaderFactory<MetadataTest, InputStream> { // 注意!这里不要把Context作为Factory的长期成员变量! private final Context tempContext; // 构造方法临时接收Context,只为中转给ModelLoader public RawModelLoaderFactory(Context context) { this.tempContext = context; } @Override public ModelLoader<MetadataTest, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) { // 直接把临时Context传给ModelLoader,Factory完成使命就没用了 return new RawModelLoader(tempContext); } @Override public void teardown() { // 无需清理Context,因为Factory没长期持有它 } }
2. 实现自定义ModelLoader
public class RawModelLoader implements ModelLoader<MetadataTest, InputStream> { private final Context appContext; public RawModelLoader(Context context) { // 关键操作:转成ApplicationContext,避免持有Activity等上下文导致泄漏 this.appContext = context.getApplicationContext(); } @Nullable @Override public LoadData<InputStream> buildLoadData(@NonNull MetadataTest model, int width, int height, @NonNull Options options) { // 这里就可以安全地用appContext做资源访问等操作了 InputStream inputStream = appContext.getResources().openRawResource(model.getResId()); return new LoadData<>(new ObjectKey(model), new DataFetcher<InputStream>() { @Override public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { callback.onDataReady(inputStream); } @Override public void cleanup() { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } @Override public void cancel() {} @NonNull @Override public Class<InputStream> getDataClass() { return InputStream.class; } @NonNull @Override public DataSource getDataSource() { return DataSource.LOCAL; } }); } @Override public boolean handles(@NonNull MetadataTest model) { return true; } }
3. 注册组件
还是用你原来的注册方式就行:
@Override public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { registry.append(MetadataTest.class, InputStream.class, new RawModelLoaderFactory(context)); }
为什么要这么做?
- Factory是Glide Registry的一部分,生命周期很长,如果存Activity Context,当Activity销毁后,Factory还持有引用就会造成内存泄漏;
- 我们让Factory只做临时中转,把Context传给ModelLoader后,ModelLoader转存ApplicationContext(ApplicationContext的生命周期和应用一致,不会泄漏),既满足了业务需要,又保证了内存安全。
内容的提问来源于stack exchange,提问作者Anthony
相关产品推荐
相关产品推荐

