图像适配器泄漏内存
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 OkHttpClient
和 new 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 找出哪些参考文献仍然存在以及由谁保存。
作为字段的适配器存在漏洞。视图包含包含视图的上下文。碎片甚至是更糟糕的罪犯。 ListActivities 是一个早就应该弃用的 API1 工具。都非常容易泄漏,但这就是 Android 方式。
我有一个简单的 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()
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 OkHttpClient
和 new 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 找出哪些参考文献仍然存在以及由谁保存。
作为字段的适配器存在漏洞。视图包含包含视图的上下文。碎片甚至是更糟糕的罪犯。 ListActivities 是一个早就应该弃用的 API1 工具。都非常容易泄漏,但这就是 Android 方式。