图像适配器泄漏内存

Image adapter leaking memory

我有一个简单的 ListActivity 可以显示图像,我为 Picasso Builder[=58= 初始化了我的 OkHttpClient ] 在 ImageAdapter 的构造函数中 class:

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() 我使用 Picasso 在 ImageView 中加载图像:

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

它运行良好,但在设备旋转时,我看到 堆大小 有时增长缓慢,有时增长迅速,有时保持稳定。它很少掉落。我是在泄漏内存还是代码有问题?

编辑: 我在 getView()

中 Picasso 的调用后插入了这段代码
if (BuildConfig.DEBUG) {
    Log.i("HEAP SIZE",
    String.valueOf((Runtime.getRuntime().totalMemory() / 1024)
    - (Runtime.getRuntime().freeMemory() / 1024)));
}

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

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

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

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

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

结果: 最后我创建了我的 PicassoInstance class,它创建了一个唯一的静态 Picasso 单例并将其设置为 Picasso 库的单例。然后我在我的适配器构造函数中设置它

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();
}

}

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

YourClass.getMyPicassoSingleton().with...

否则,您将为那些 picasso 实例保留单独的缓存等

编辑:如下所述,您也可以调用

picasso.setSingletonInstance(myPicasso);

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

我不能关闭它,因为它太宽泛了,但我建议你 took a memory dump and gave it a long hard look in Eclipse Memory Analizer Tool 找出哪些参考文献仍然存在以及由谁保存。

这也是a great write up on it

作为字段的适配器存在漏洞。视图包含包含视图的上下文。碎片甚至是更糟糕的罪犯。 ListActivities 是一个早就应该弃用的 API1 工具。都非常容易泄漏,但这就是 Android 方式。