Android ViewPager/PagerAdapter ImageView OutOfMemoryError
Posted
技术标签:
【中文标题】Android ViewPager/PagerAdapter ImageView OutOfMemoryError【英文标题】: 【发布时间】:2012-12-08 19:59:21 【问题描述】:我在 *** 和其他地方看到过各种类似的问题,但这些解决方案似乎都没有解决我的问题,如下所示:
我有一个带有简单 PagerAdapter 的 ViewPager。每个“页面”都有一个与之关联的 XML 布局,其中至少包含一个 TextView、一个 ImageView,有时还包含另一个 TextView。与 ImageView 关联的图像通常在一个维度上为 1000px,在另一个维度上更小,但它们是 8 位索引 gif,因此它们都不会大于 20kB。我将它们设置为 Drawable,它们会适当地缩小以适应分配给它们的空间。
只有 6 页。当我启动活动时,它工作正常,我什至可以翻到最后一页,但是如果我一直向右翻,然后一直向左翻,我会得到一个 OutOfMemoryError。这对我来说没有意义,因为我认为 ViewPager 应该销毁超过 1 个屏幕以外的页面,并且我正在按照其他地方的建议处理 destroyItem()。
这是 PagerAdapter 的代码:
package doop.doop.dedoop;
import doop.doop.dedoop.R.layout;
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
public class HelpPagerAdapter extends PagerAdapter
private Context applicationContext;
private Context activityContext;
public HelpPagerAdapter(Context appContext, Context activContext)
applicationContext = appContext;
activityContext = activContext;
public int getCount()
return 6;
public Object instantiateItem(ViewGroup collection, int position)
LayoutInflater inflater = (LayoutInflater) collection.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
int resId = 0;
int ivId = 0;
int drawId = 0;
switch (position)
case 0:
resId = R.layout.help1;
ivId = R.id.helpImage1;
drawId = R.drawable.left_col_highlight;
break;
case 1:
resId = R.layout.help2;
ivId = R.id.helpImage2;
drawId = R.drawable.right_col_highlight;
break;
case 2:
resId = R.layout.help3;
ivId = R.id.helpImage3;
drawId = R.drawable.p2win;
break;
case 3:
resId = R.layout.help4;
ivId = R.id.helpImage4;
drawId = R.drawable.first_move;
break;
case 4:
resId = R.layout.help5;
ivId = R.id.helpImage5;
drawId = R.drawable.screen_bottom;
break;
case 5:
resId = R.layout.help6;
ivId = R.id.helpImage6;
drawId = R.drawable.illegal_move;
break;
View view = inflater.inflate(resId, null);
collection.addView(view, 0);
if (ivId != 0 && drawId != 0)
ImageView iv = (ImageView) view.findViewById(ivId);
iv.setImageDrawable(activityContext.getResources().getDrawable(drawId));
return view;
@Override
public void destroyItem(ViewGroup container, int position, Object object)
container.removeView((View)object);
@Override
public boolean isViewFromObject(View arg0, Object arg1)
return arg0 == ((View) arg1);
这是其中一种页面布局的示例:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/helpPageLayout" >
<TextView
style="@style/helpText"
android:text="@string/help2" />
<ImageView android:id="@+id/helpImage2"
style="@style/helpImage" />
</LinearLayout>
这是整个活动所在的活动:
package doop.doop.dedoop;
import android.app.Activity;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
public class Help extends Activity
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_help);
// Set up the ViewPager and its kin
HelpPagerAdapter adapter = new HelpPagerAdapter(getApplicationContext(), this);
ViewPager helpPager = (ViewPager) findViewById(R.id.helpPager);
helpPager.setAdapter(adapter);
public void exitHelp(View view)
finish();
这是该 Activity 的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mainHelp"
android:orientation="vertical"
android:layout_
android:layout_>
<android.support.v4.view.ViewPager android:id="@+id/helpPager"
android:layout_
android:layout_
android:layout_weight="1" />
<Button
android:text="@string/helpBack"
android:onClick="exitHelp"
style="@style/mainMenuButtons" />
</LinearLayout>
最后,这是错误:
12-08 11:55:23.114: E/AndroidRuntime(9090): FATAL EXCEPTION: main
12-08 11:55:23.114: E/AndroidRuntime(9090): java.lang.OutOfMemoryError
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.graphics.Bitmap.nativeCreate(Native Method)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:618)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:593)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:445)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:775)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.content.res.Resources.loadDrawable(Resources.java:1968)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.content.res.Resources.getDrawable(Resources.java:677)
12-08 11:55:23.114: E/AndroidRuntime(9090): at doop.doop.dedoop.HelpPagerAdapter.instantiateItem(HelpPagerAdapter.java:69)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:801)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.support.v4.view.ViewPager.populate(ViewPager.java:962)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.support.v4.view.ViewPager.populate(ViewPager.java:881)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.support.v4.view.ViewPager$3.run(ViewPager.java:237)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.os.Handler.handleCallback(Handler.java:605)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.os.Handler.dispatchMessage(Handler.java:92)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.os.Looper.loop(Looper.java:137)
12-08 11:55:23.114: E/AndroidRuntime(9090): at android.app.ActivityThread.main(ActivityThread.java:4514)
12-08 11:55:23.114: E/AndroidRuntime(9090): at java.lang.reflect.Method.invokeNative(Native Method)
12-08 11:55:23.114: E/AndroidRuntime(9090): at java.lang.reflect.Method.invoke(Method.java:511)
12-08 11:55:23.114: E/AndroidRuntime(9090): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
12-08 11:55:23.114: E/AndroidRuntime(9090): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
12-08 11:55:23.114: E/AndroidRuntime(9090): at dalvik.system.NativeStart.main(Native Method)
我想可能值得注意的是,这仅发生在我目前正在测试的两台设备中的一台上(三星 Galaxy SIII,但不是 Kindle Fire HD 8.9)。
【问题讨论】:
同样的问题:在 Galaxy S2 上工作,在 Galaxy Tab 10 上内存不足。滚动到右边,好的,回到左边 OOM。 【参考方案1】:您可以尝试以下几点:
使用 PNG 作为图像,尝试一些 png-crunch-app(或使用 photoshop > 导出到网络) 回收您的观点:将您从容器中删除的视图保存在 ArrayList 中的 destroyItem() 中。在 instantiateItem() 期间,首先检查您的数组列表中是否有未使用的视图,如果有,请使用该视图。如果没有,请充气。这样,您只需增加 3 个视图,并回收其余视图。查看here 了解有关 ViewHolders 的更多信息,以防止调用 findViewById() 过多。
更高效地加载位图:使用带有 inJustDecodeBounds = true 的 BitmapFactory.Options 来解码您的位图并检查位图的边界。 然后使用 inSampleSize 解码较小的版本。如果这与您相关,请查看here 了解更多信息。
主要项目利用 pagerAdapter 的setPrimaryItem()。并在那里加载位图。 这样,您只加载当前可见项目的位图,从而减少内存负载。但是有点破坏了预加载上一页/下一页以获得更流畅的滚动和更好的用户体验的意义。
示例代码(未经测试!)
private ImageView primaryView;
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object)
super.setPrimaryItem(container, position, object);
if (this.primaryView == object)
//this method might be called a lot, so don't do anything unless the primaryView changes!
return ;
if (this.primaryView != null)
//recycle the previous bitmap to clear memory
((BitmapDrawable)this.primaryView.getDrawable()).getBitmap().recycle();
//set new primary view
this.primaryView = (ImageView) object;
int resourceId = 0;//get image resource id here
//load bitmap here efficiently
this.primaryView.setImageResource(resourceId);
【讨论】:
我还要说... 1/ 运行 DDMS 并确保碎片被销毁。例如,也许他们持有对使他们不适合 gc 的上下文的硬引用。作为快速测试,您可以在 Pagelistener.settle 上发出 System.gc 并来回滚动前 2 页...在 logcat 下检查您是否没有泄漏内存...我很确定是这种情况... 【参考方案2】:helpPager.setOffscreenPageLimit(MAX_PAGES);
当页面超出这个范围时它会销毁。
【讨论】:
根据文档,屏幕外页面限制的默认值为 1,所以我的印象是手动设置它不应该对内存有任何帮助。无论如何我都试过了,值是1,然后是0,都没有解决问题。很明显,这些页面,或者至少是 Drawable,没有被破坏。作为一个实验,我将它设置为 5,当我第一次使用 ViewPager 加载活动时,应用程序崩溃了。 也许它与 gif 有关,谷歌不鼓励使用它们。 嗯,它们最初是 PNG 文件,我也遇到了同样的问题。我将它们转换为索引 gif 以使它们更小,希望更小的文件可以减少内存负载。【参考方案3】:在清单文件的应用程序标签中设置 android:largeHeap="true" 解决了我在 viewpager 中出现 outOfMemory 异常的问题。
【讨论】:
好吧...如果你有内存泄漏,大堆只会在 OOM 之前给你一些时间。我不会无缘无故地推荐使用 largeheap。请注意,它会影响您的应用程序速度,也会对整个系统产生不利影响。 是的,根据 rupps,这不是解决方案。 那么我应该用什么来解决这个问题呢? Jelle 提供了迄今为止最好的答案。以上是关于Android ViewPager/PagerAdapter ImageView OutOfMemoryError的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )