Утечка памяти адаптера изображения

у меня простой ListActivity это показывает изображения, и я inizialize мой OkHttpClient на Пикассо Строитель в конструкторе класса ImageAdapter:

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

он работает хорошо, но при вращении устройства я вижу, что размер "кучи" иногда медленно растет, иногда быстро и иногда остается стабильным. Только изредка капли. У меня утечка памяти или что-то не так в коде?

EDIT: Я вставил этот код после звонка Пикассо в getView()

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

и я обнаружил, что рост размера кучи происходит в getView() после загрузки растрового изображения в ImageView. Что случилось?

правка 2: пытался установить статический ImageAdapter, ничего не меняется

EDIT 3: пробовал с RecyclerView вместо ListView, такое же поведение: размер кучи непрерывно растет при прокрутке списка изображений, шагая на 30-40 байт при каждом onBindViewHolder(). После вращения устройства размер кучи увеличивается иногда даже на 2-3 Мбайт. Редко падает.

почему размер кучи медленно, но непрерывно растет и почему я пропускаю некоторый кэш или некоторые кэшированные растровые изображения после вращения устройства?

обновление: пробовал адаптер без кода в конструкторе (то есть без new OkHttpClient и new Picasso.Builder), он работает и размер кучи теперь падает, оставаясь стабильным. Тогда как правильно инициализировать клиента с помощью управления заголовками cookies?

результат: наконец, я создал свой класс PicassoInstance, который создает уникальный статический синглтон Пикассо и устанавливает его как синглтон библиотеки Пикассо. Затем я установил его в своем конструкторе адаптера

PicassoInstance.setPicassoSingleton(context);

он работает хорошо, и это правильный путь I надеяться.

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

}

2 ответов


экземпляр picasso возвращается из PicassoBuilder.build () должен быть одноэлементным, и когда вам нужно использовать picasso во всем приложении, вы должны получить доступ к этому одноэлементному, а не Пикассо.с... вы должны получить доступ

YourClass.getMyPicassoSingleton().with...

в противном случае вы держите отдельные кэши и т. д. Для этих экземпляров picasso

edit: как я отметил ниже, вы также можете вызвать

picasso.setSingletonInstance(myPicasso);

справа, где вы вызываете метод сборки выше, который также решит ваша проблема, не держась за синглтона самостоятельно. это, вероятно, более чистое решение


Я не могу закрыть его, как слишком широкий, но я бы рекомендовал вам взял дамп памяти и дал ему длинный твердый взгляд в Eclipse Memory Analizer Tool чтобы найти, какие ссылки все еще сохраняются в живых и кто.

Это тоже отличная запись на нем.

адаптеры как поля протекают. Представления содержат контекст, содержащий представления. А осколки-еще хуже. ListActivities были инструментом API1, который должен был быть давно устарел. Все очень подвержены утечке, но это путь Android.