【问题标题】:Image adapter leaking memory图像适配器泄漏内存
【发布时间】:2015-07-03 14:40:04
【问题描述】:

我有一个显示图像的简单 ListActivity,我在 ImageAdapter 类的构造函数中为 Picasso Builder 初始化了我的 OkHttpClient: p>

picassoClient = new OkHttpClient();
picassoClient.interceptors().add(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request newRequest = chain
            .request()
            .newBuilder()
            .addHeader("Cookie","xyz")
            .build();

        return chain.proceed(newRequest);
    }
});

new Picasso.Builder(context).downloader(new OkHttpDownloader(picassoClient)).build();

然后在getView()我用毕加索在ImageView中加载图片:

Picasso.with(context).load(xyzUrl).fit().centerCrop().into(vImage);

它运作良好,但在设备的轮换中,我看到 堆大小 有时会缓慢增长,有时会快速增长,有时会保持稳定。只是很少会掉落。我是在泄漏内存还是代码有问题?

编辑: 我在getView()getView()毕加索的电话之后插入了这段代码

if (BuildConfig.DEBUG) {
    Log.i("HEAP SIZE",
    String.valueOf((Runtime.getRuntime().totalMemory() / 1024)
    - (Runtime.getRuntime().freeMemory() / 1024)));
}

我发现堆大小的增长发生在将位图加载到 ImageView 后的 getView() 中。 怎么了?

编辑 2: 尝试设置静态 ImageAdapter,没有任何变化

编辑 3: 尝试使用 RecyclerView 而不是 ListView,同样的行为:当滚动图像列表时,堆大小不断增长,每 onBindViewHolder() 步进 30-40 个字节。设备的旋转堆大小有时会增长 2-3 MB。很少掉落。

为什么堆大小缓慢但持续增长,为什么我会在设备旋转后泄漏一些缓存或一些缓存的位图?

更新: 尝试了没有构造函数中代码的适配器(即没有 new OkHttpClientnew Picasso.Builder),它可以工作并且堆大小现在下降得很好,保持稳定。那么,用cookie头管理初始化客户端的正确方法是什么?

简明扼要: 最后我创建了我的 PicassoInstance 类,它创建了一个独特的静态毕加索单例并将其设置为毕加索图书馆的单例。然后我在我的适配器构造函数中设置它

PicassoInstance.setPicassoSingleton(context);

效果很好,我希望这是正确的方式。

public class PicassoInstance {
private static Picasso myPicassoInstance = null;

public static void setPicassoSingleton(Context context) {
    if (myPicassoInstance == null) {
        myPicassoInstance = createMyPicassoInstance(context);
        Picasso.setSingletonInstance(myPicassoInstance);
        if (BuildConfig.DEBUG) {
            Log.i("PICASSO INSTANCE", "CREATED");
        }
    }
}

private static Picasso createMyPicassoInstance(Context context) {
    OkHttpClient myOkHttpClient = new OkHttpClient();
    myOkHttpClient.interceptors().add(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request newRequest = chain.request().newBuilder()
                    .addHeader("Cookie", "xyz").build();
            if (BuildConfig.DEBUG) {
                Log.i("ON INTERCEPT", "COOKIE ADDED");
            }
            return chain.proceed(newRequest);
        }
    });

    return new Picasso.Builder(context).downloader(
            new OkHttpDownloader(myOkHttpClient)).build();
}

}

【问题讨论】:

  • 请展示你的生命周期方法。
  • 所以你每次都在 onCreate() 中创建一个新的适配器?
  • 那我猜适配器泄漏了一些东西。我在 onPause() 中明确地将其设为空,有什么变化吗?如果没有,可能值得显示您的适配器代码。您是否将其绑定到其他任何东西?
  • 毕加索将图像缓存在内存中以便快速检索,所以我的猜测是它只是保留对图像的引用,直到感觉需要删除它们。在您产生 OOM 错误之前,我不会担心它。
  • 跟踪内存分配的最简单方法是使用 android 的 MAT 实用程序。它给出了未从内存中释放的类对象的列表。你可以在这里阅读它的用法:android-developers.blogspot.in/2011/03/…

标签: android android-recyclerview listadapter picasso okhttp


【解决方案1】:

从 PicassoBuilder.build() 返回的 picasso 实例应该是一个单例,当您需要在整个应用程序中使用 picasso 时,您应该访问该单例,而不是 Picasso.with... 您应该访问

YourClass.getMyPicassoSingleton().with...

否则,您将为这些毕加索实例保留单独的缓存等

编辑:正如我在下面提到的,您也可以调用

picasso.setSingletonInstance(myPicasso);

就在您调用上面的构建方法的地方,这也可以解决您的问题,而无需自己持有单例。这可能是一个更清洁的解决方案

【讨论】:

  • 我认为毕加索也是这样做的,并且每次都会给你一个“单身”
  • github.com/square/picasso/blob/master/picasso/src/main/java/com/… 如果您自己调用构建器,它不会设置其内部单例。因此,如果您调用 builder 方法并构建自己的方法,然后继续使用 Picasso.with,您已经创建了两个实例。看看代码就很简单了。请注意,您可以使用 setsingletoninstance 在 picasso 上设置单例,这对于 OP 来说同样有效,并且也可以解决问题
  • 很高兴您发表了评论,所以我实际上看了课程并看到了一个更简单的解决方案哈哈
  • 好吧,请教一个问题:Picasso.setSingletonInstance(myPicasso) 必须调用一次,那么我如何在设备旋转后检查此行为,也必须是我自己的实例 myPicasso 静态的?跨度>
  • 我不清楚你的意思。你只调用一次 setsingletoninstance,就像你创建 okhttp 客户端时一样。然后在旋转时,您应该只使用 picasso.with 调用并重置图像视图。这有意义吗?
【解决方案2】:

我不能将它关闭得太宽泛,但我建议您 took a memory dump 并在 Eclipse Memory Analizer Tool 中仔细查看,以找出哪些引用仍然存在以及由谁保存。

这也是a great write up on it

作为字段的适配器是泄漏的。视图包含包含视图的上下文。碎片是更严重的罪犯。 ListActivities 是一个 API1 工具,早就应该弃用了。所有这些都非常容易泄漏,但这是 Android 的方式。

【讨论】:

    猜你喜欢
    • 2016-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-27
    相关资源
    最近更新 更多