2012-11-27 116 views
15

我正在编写一个应用程序,显示最终有几个标记的全景图片以显示有关某些点的信息。Android ViewPager带图像:内存泄露/应用程序崩溃

随着大图像崩溃了应用程序(我还在显示大图的应用程序中有另一项活动),我现在试图使用ViewPager将全景图显示为一系列页面。

我已经设法以6位显示图片,我认为事情进展顺利,但现在应用程序在内存用完后几次刷新(大约7到8)后崩溃。

我拉我的头发为什么这是因为我认为我的物品一旦他们离开屏幕就会被破坏? 我是一个绝对的新手,我很抱歉,如果我是一个时间浪费。我花了整整一天的时间阅读并尝试从这里或其他地方获得解决方案,而且我也不明智。

这里是我的代码: 活动PanoramaView

public class PanoramaView extends Activity { 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.panorama); 
    MyPagerAdapter adapter = new MyPagerAdapter(); 
    ViewPager myPager = (ViewPager) findViewById(R.id.mysixpanelpager); 
    myPager.setAdapter(adapter); 
    myPager.setCurrentItem(2); 
} 


} 

MyPagerAdapter

public class MyPagerAdapter extends PagerAdapter { 
    public int getCount() { 
     return 6; 
    } 
    public Object instantiateItem(View collection, int position) { 
     LayoutInflater inflater = (LayoutInflater) collection.getContext() 
       .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     int resId = 0; 
     switch (position) { 
     case 0: 
      resId = R.layout.farleft; 
      break; 
     case 1: 
      resId = R.layout.left; 
      break; 
     case 2: 
      resId = R.layout.middle; 
      break; 
     case 3: 
      resId = R.layout.right; 
      break; 
     case 4: 
      resId = R.layout.farright; 
      break; 
     case 5: 
      resId = R.layout.farfarright; 
      break; 
     } 
     //ImageView imageView = new ImageView(getApplicationContext()); 
     //imageView.findViewById(R.id.imageView); 
     //imageView.setImageBitmap(BitmapFactory.decodeResource(getResources(), ids[position])); 

     View view = inflater.inflate(resId, null); 
     ((ViewPager) collection).addView(view, 0); 
     return view; 
    } 
    @Override 
    public void destroyItem(View collection, int position, Object o) { 
     View view = (View)o; 
     ((ViewPager) collection).removeView(view); 
     view = null; 
    } 

    @Override 
    public boolean isViewFromObject(View arg0, Object arg1) { 
     return arg0 == ((View) arg1); 
    } 
    @Override 
    public Parcelable saveState() { 
     return null; 
    } 
} 

我的主要布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:orientation="vertical" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent"> 
<android.support.v4.view.ViewPager 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:id="@+id/mysixpanelpager"/> 
</LinearLayout> 

我保证我会成为一个有用的成员从现在开始(甚至更多,所以一旦我真的知道我在做什么)。

编辑: - 在第一个活动中,我显示的图像是552kb。 - 我在此活动中显示的六幅图像(PanoramaView)在309到500kb之间。 - 我在Eclipse中使用了一个分配跟踪器,我只能看到内存已满,但确切的数据并不清晰 - 显示7或8张图像后发生崩溃(基本上在刷几次后和第四次后)

这里是我已经尝试设置关闭屏幕页面限额,这并没有帮助farfarright.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:orientation="vertical" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" > 

<ImageView 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:id="@+id/imageView" 
    android:src="@drawable/panorama6" 
    android:adjustViewBounds="true" 
    android:contentDescription="@string/panorama" > 

</ImageView> 
</LinearLayout> 

的代码。

我发现this关于另一篇文章的内存管理的链接,我今晚看看它。

编辑:这里是logcat的输出

11-28 21:17:42.551: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 51K, 53% free 2558K/5379K, external 2002K/2137K, paused 65ms 
11-28 21:17:43.261: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 1K, 53% free 2557K/5379K, external 3297K/4118K, paused 44ms 
11-28 21:17:47.741: W/KeyCharacterMap(328): No keyboard for id 0 
11-28 21:17:47.741: W/KeyCharacterMap(328): Using default keymap: /system/usr/keychars/qwerty.kcm.bin 
11-28 21:17:49.141: D/DFSAPP(328): my button id before is 2 
11-28 21:17:49.691: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 36K, 52% free 2614K/5379K, external 15576K/15708K, paused 50ms 
11-28 21:17:54.571: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 12K, 52% free 2616K/5379K, external 17386K/17735K, paused 39ms 
11-28 21:17:54.661: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 0K, 52% free 2616K/5379K, external 17386K/17735K, paused 61ms 
11-28 21:17:54.711: I/dalvikvm-heap(328): Clamp target GC heap from 25.629MB to 24.000MB 
11-28 21:17:54.711: D/dalvikvm(328): GC_FOR_MALLOC freed <1K, 52% free 2616K/5379K, external 18975K/21023K, paused 42ms 
11-28 21:18:03.751: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 6K, 52% free 2616K/5379K, external 18269K/20317K, paused 46ms 
11-28 21:18:03.822: I/dalvikvm-heap(328): Clamp target GC heap from 25.628MB to 24.000MB 
11-28 21:18:03.852: D/dalvikvm(328): GC_FOR_MALLOC freed <1K, 52% free 2615K/5379K, external 18975K/20317K, paused 32ms 
11-28 21:18:04.131: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed <1K, 52% free 2615K/5379K, external 17386K/19434K, paused 49ms 
11-28 21:18:04.191: I/dalvikvm-heap(328): Clamp target GC heap from 25.628MB to 24.000MB 
11-28 21:18:04.201: D/dalvikvm(328): GC_FOR_MALLOC freed 0K, 52% free 2615K/5379K, external 18975K/19434K, paused 34ms 
11-28 21:18:07.301: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 1K, 52% free 2616K/5379K, external 18269K/19434K, paused 46ms 
11-28 21:18:07.381: I/dalvikvm-heap(328): Clamp target GC heap from 25.801MB to 24.000MB 
11-28 21:18:07.401: D/dalvikvm(328): GC_FOR_MALLOC freed <1K, 52% free 2616K/5379K, external 19152K/19434K, paused 38ms 
11-28 21:18:07.611: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed <1K, 52% free 2615K/5379K, external 18159K/19434K, paused 47ms 
11-28 21:18:07.681: I/dalvikvm-heap(328): Clamp target GC heap from 25.801MB to 24.000MB 
11-28 21:18:07.681: D/dalvikvm(328): GC_FOR_MALLOC freed 0K, 52% free 2615K/5379K, external 19152K/19434K, paused 36ms 
11-28 21:18:18.901: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 5K, 52% free 2616K/5379K, external 18269K/19434K, paused 57ms 
11-28 21:18:18.972: I/dalvikvm-heap(328): Clamp target GC heap from 25.802MB to 24.000MB 
11-28 21:18:18.991: D/dalvikvm(328): GC_FOR_MALLOC freed <1K, 52% free 2616K/5379K, external 19152K/19434K, paused 33ms 
11-28 21:18:19.181: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 1K, 52% free 2615K/5379K, external 18159K/19434K, paused 55ms 
11-28 21:18:19.251: I/dalvikvm-heap(328): Clamp target GC heap from 25.801MB to 24.000MB 
11-28 21:18:19.251: D/dalvikvm(328): GC_FOR_MALLOC freed 0K, 52% free 2615K/5379K, external 19152K/19434K, paused 33ms 
11-28 21:18:21.551: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 1K, 52% free 2616K/5379K, external 18975K/19434K, paused 46ms 
11-28 21:18:21.581: E/dalvikvm-heap(328): 1627200-byte external allocation too large for this process. 
11-28 21:18:21.621: I/dalvikvm-heap(328): Clamp target GC heap from 25.629MB to 24.000MB 
11-28 21:18:21.621: E/GraphicsJNI(328): VM won't let us allocate 1627200 bytes 
11-28 21:18:21.631: D/dalvikvm(328): GC_FOR_MALLOC freed 0K, 52% free 2616K/5379K, external 18975K/19434K, paused 34ms 
11-28 21:18:21.641: D/AndroidRuntime(328): Shutting down VM 
11-28 21:18:21.641: W/dalvikvm(328): threadid=1: thread exiting with uncaught exception (group=0x40015560) 
11-28 21:18:21.732: E/AndroidRuntime(328): FATAL EXCEPTION: main 
11-28 21:18:21.732: E/AndroidRuntime(328): android.view.InflateException: Binary XML file line #7: Error inflating class <unknown> 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.createView(LayoutInflater.java:518) 
11-28 21:18:21.732: E/AndroidRuntime(328): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:568) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.rInflate(LayoutInflater.java:623) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.inflate(LayoutInflater.java:408) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.inflate(LayoutInflater.java:320) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.inflate(LayoutInflater.java:276) 
11-28 21:18:21.732: E/AndroidRuntime(328): at com.businesbike.dfp.MyPagerAdapter.instantiateItem(MyPagerAdapter.java:43) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:692) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.support.v4.view.ViewPager.populate(ViewPager.java:849) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.support.v4.view.ViewPager.populate(ViewPager.java:772) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.support.v4.view.ViewPager.completeScroll(ViewPager.java:1539) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.support.v4.view.ViewPager.computeScroll(ViewPager.java:1422) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.drawChild(ViewGroup.java:1562) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.drawChild(ViewGroup.java:1644) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.View.draw(View.java:6883) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.widget.FrameLayout.draw(FrameLayout.java:357) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.drawChild(ViewGroup.java:1646) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.drawChild(ViewGroup.java:1644) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.View.draw(View.java:6883) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.widget.FrameLayout.draw(FrameLayout.java:357) 
11-28 21:18:21.732: E/AndroidRuntime(328): at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1862) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewRoot.draw(ViewRoot.java:1522) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewRoot.performTraversals(ViewRoot.java:1258) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewRoot.handleMessage(ViewRoot.java:1859) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.os.Handler.dispatchMessage(Handler.java:99) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.os.Looper.loop(Looper.java:123) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.app.ActivityThread.main(ActivityThread.java:3683) 
11-28 21:18:21.732: E/AndroidRuntime(328): at java.lang.reflect.Method.invokeNative(Native Method) 
11-28 21:18:21.732: E/AndroidRuntime(328): at java.lang.reflect.Method.invoke(Method.java:507) 
11-28 21:18:21.732: E/AndroidRuntime(328): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 
11-28 21:18:21.732: E/AndroidRuntime(328): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 
11-28 21:18:21.732: E/AndroidRuntime(328): at dalvik.system.NativeStart.main(Native Method) 
11-28 21:18:21.732: E/AndroidRuntime(328): Caused by: java.lang.reflect.InvocationTargetException 
11-28 21:18:21.732: E/AndroidRuntime(328): at java.lang.reflect.Constructor.constructNative(Native Method) 
11-28 21:18:21.732: E/AndroidRuntime(328): at java.lang.reflect.Constructor.newInstance(Constructor.java:415) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.createView(LayoutInflater.java:505) 
11-28 21:18:21.732: E/AndroidRuntime(328): ... 36 more 
11-28 21:18:21.732: E/AndroidRuntime(328): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.Bitmap.nativeCreate(Native Method) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.Bitmap.createBitmap(Bitmap.java:477) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.Bitmap.createBitmap(Bitmap.java:444) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:349) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:498) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:473) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:336) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.content.res.Resources.loadDrawable(Resources.java:1709) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.content.res.TypedArray.getDrawable(TypedArray.java:601) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.widget.ImageView.<init>(ImageView.java:118) 
11-28 21:18:21.732: E/AndroidRuntime(328): at android.widget.ImageView.<init>(ImageView.java:108) 
11-28 21:18:21.732: E/AndroidRuntime(328): ... 39 more 

万一有人遇到这个帖子,这里是什么似乎已经解决了它: 我的代码的代码和我下面的答案改变也将我的全景图片切分成较小的位,因此每张图片现在都低于300kb。

+0

你是什么意思大图像?你能发布堆栈跟踪吗?它是在第一张图像上崩溃还是成功显示一些图像而不是崩溃?你还可以发布布局“farfarright” – Mikhaili

+0

我已经在上面添加了一些更多细节。任何帮助是极大的赞赏。 – Workbarby

+0

发布堆栈跟踪 – hardartcore

回答

3

因此,使用

@Override 
public Object instantiateItem(ViewGroup collection, int position) 
@Override 
public void destroyItem(ViewGroup collection, int position, Object view) 

代替

public Object instantiateItem(View collection, int position) 
public void destroyItem(View collection, int position, Object view) 

2测试在6个图像尺寸400K每个正常工作

@Override 
public Object instantiateItem(ViewGroup collection, int position) 
{ 
    LayoutInflater inflater = (LayoutInflater) collection.getContext() 
      .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 



    int resId = 0; 
    switch (position) { 
    case 0: 
     resId = R.layout.farleft; 
     break; 
    case 1: 
     resId = R.layout.left; 
     break; 
    case 2: 
     resId = R.layout.middle; 
     break; 
    case 3: 
     resId = R.layout.right; 
     break; 
    case 4: 
     resId = R.layout.farright; 
     break; 
    case 5: 
     resId = R.layout.farfarright; 
     break; 
    } 

View view = (View) inflater.inflate(resId, null); 


//get Image view from layout 
//ImageView imageView = (ImageView) view.findViewById(resId); 

//imageView.setImageResource(resId); 

    collection.addView(view, 0); 

    return view; 
} 

@Override 
public void destroyItem(ViewGroup collection, int position, Object view) 
{ 
    collection.removeView((View) view); 
} 

3有用的链路PageAdapter

古德勒克

+0

非常感谢你的代码和链接。我已为您交换了我的代码,问题仍然存在。我没有意识到我实际上使用了弃用的东西。我会看看我是否会通过禁用位来得到更多。 – Workbarby

+0

尝试在我的代码中打开关闭部分“imageview” – Mikhaili

+0

@Workbarby:您将此标记为答案,那么您可以告诉我什么是确切的解决方案吗?我也面临同样的问题,我对解决方案一无所知。我也尝试了很多方法,但都失败了。我尝试过使用毕加索或Universal Image Loader,但仍未成功。 –

2
pager.setOffscreenPageLimit(MAX_PAGE); //pager is the ViewPager instance 

这里MAX_PAGE是可以保留在可见性范围之外的页数。除此之外的任何其他内容都将被销毁,并且只有在用户滑回到附近位置时才会被重新创建。

4

有据可查的问题,即使你似乎已经解决了吧,这里是潜在有用的信息一点点。

从你logcat,我发现你正在开发(或至少测试)姜饼(因为蜂窝向前不包括输出中的“外部”部分。这很重要,只是因为它突出了发生了什么事在姜饼中,位图数据被放置在本机内存中,堆外,然后,一个指针将数据放在堆中,以及其他一些参考信息。删除对位图的每个引用将释放参考信息(在某些时候,当System.gc()运行)。但是,位图数据将永远不会被释放,除非您的设备被命中为teroid - 或(不那么戏剧性),您可以在该位图上调用recycle()方法。应该注意的是,这个调用确实会释放本地内存,所以当需要重新创建位图时,ViewPager(最有可能在instantiateItem之间。蜂窝和后续将位图数据内的堆栈,这是少一点恼人的。data仍然是只有当你发布recycle()位图(所以你必须深入挖掘,使用DDMS为了确定发生了什么 - 例如,我是目前在JB和ICS中,4.0.3的行为与4.0.4不同,但我离题)

这可能是矫枉过正,但我​​的解决方案是实现一个类来跟踪我的位图,确信我已经回收了它们 - 就你而言,这一点当您在ViewPager中使用destroyItem()时会发生。

下面是我用来跟踪事物的(有点行人的)类。

public class BitmapManager { 

private final String TAG = "DEBUG -- " + ClassUtils.getShortClassName(this.getClass()); 
Context mContext = null; 

private class BitmapVectorEntry { 
    public Bitmap bm = null; 
    public String name = null; 
} 

Vector<BitmapVectorEntry> mBitmapVector = new Vector<BitmapVectorEntry>(); 

public BitmapManager(Context aContext) { 
    mContext = aContext; 
} 

public void setContext(Context aContext) { 
    mContext = aContext; 
} 

public void registerBitmap(String name, Bitmap b) { 

    if(mBitmapVector == null) { 
     mBitmapVector = new Vector<BitmapVectorEntry>(); 
    } 
    if(b == null) { 
     Log.e(TAG, "Bitmap is NULL!! "); 
     return; 
    } 
    // Log.d(TAG, "  ~~~~~~ Registering ["+name+"] ["+b+"]"); 
    BitmapVectorEntry be = new BitmapVectorEntry(); 
    be.bm = b; 
    be.name = name; 
    mBitmapVector.add(be); 
} 

public void registerBitmapForBackgroundDrawable(String name, View v) { 
    if(v != null) { 
     Drawable d = v.getBackground(); 
     if(d != null) { 
      if(d instanceof BitmapDrawable) { 
       Bitmap bm = ((BitmapDrawable) d).getBitmap(); 
       if(bm != null) { 
        // Log.w(TAG, "  ~~~~ Registering Background Bitmap [" + bm + "]"); 
        registerBitmap(name, bm); 
       } else { 
        Log.w(TAG, " ~~~~ Background does not have a bitmap in the BitmapDrawable (Probably, but not necessarily, and error)"); 
       } 
      } else { 
       Log.w(TAG, " ~~~~ Background does not have a BitmapDrawable (Might not be an error)"); 
      } 
     } else { 
      Log.w(TAG, " ~~~~~ Background is null, no drawable (Might not be an error)"); 
     } 
    } else { 
     Log.e(TAG, " ~~~~~ View is null, is there no background for this view?"); 
    } 
} 

// We cannot recycle certain bitmaps, like the background for the page which houses 
    // the ViewPager, since the pager reuses it, so we just delete it from vector 
public void clear(Bitmap bm) { 
    removeBitmap(bm, false); 
} 

// In most cases, when we are done with a bitmap, we want to recycle it. This is a 
    // synchronous call that frees external heap (in 2.3.x) or internal heap (3.x <). 
    // And when I say 'synchronous' I mean 'slow' and 'should not be run on the UI Thread, 
    // So be sure to throw this on an async thread 
public void recycleBitmap(Bitmap bm) { 
    removeBitmap(bm, true); 
} 

private void removeBitmap(Bitmap bm, boolean andRecycleToo) { 

    if(bm == null) { 
     Log.e(TAG, "(RECYCLE BITMAP) !!!! Bitmap is NULL!!, cannot recycle"); 
     return; 
    } 
    if(mBitmapVector == null) { 
     Log.e(TAG, "(RECYCLE BITMAP) !!!! Bitmap Vector is NULL!!"); 
     return; 
    } 

    boolean foundIt = false; 
    Bitmap targetBm = null; 
    int i = (mBitmapVector.size() - 1); 
    try { 
     for(; i >= 0; i--) { 
      BitmapVectorEntry b = mBitmapVector.get(i); 
      targetBm = b.bm; 
      if(targetBm.equals(bm)) { 
       foundIt = true; 
       if(andRecycleToo) { 
        if(!targetBm.isRecycled()) { 
         targetBm.recycle(); 
        } 
       } 
       mBitmapVector.removeElementAt(i); 
       // Log.e(TAG, "  Recycling ["+targetBm.name+"] ["+targetBm.bm+"]"); 
       break; 
      } 
     } 
    } catch(Exception e) { 

     Log.e(TAG, "Exception during recycling bitmap position ["+i+"] ["+bm+"] ["+e+"]"); 

    } finally { 

     mBitmapVector.trimToSize(); 
     if(andRecycleToo) { 
      if(!foundIt && targetBm != null) { 
       if(!targetBm.isRecycled()) { 
        targetBm.recycle(); 
       } 
       Log.e(TAG, "(RECYCLE BITMAP) ========================= !!! RECYCLING Bitmap ["+targetBm+"], was unregistered, recycled is ["+targetBm.isRecycled()+"]"); 
      } else { 
       // Log.i(TAG, "(RECYCLE BITMAP) ========================= !!! RECYCLING Bitmap ["+targBe.name+"] ["+targBe.bm+"], was registered"); 
      } 
     } 

    } 

} 

public void flush() { 
    if(mBitmapVector == null) { 
     // Log.e(TAG, "!!!! Bitmap Vector is NULL!!"); 
     return; 
    } 
    for(int i = 0; i < mBitmapVector.size(); i++) { 
     BitmapVectorEntry bme = mBitmapVector.get(i); 
     if(!bme.bm.isRecycled()) { 
      // Log.e(TAG, "Flushing Bitmap ["+bme.name+"] ["+bme.bm+"]"); 
      bme.bm.recycle(); 
     } 
    } 
    mBitmapVector.clear(); 
    mBitmapVector.trimToSize(); 

} 

public void dumpBitmaps() { 
    if(mBitmapVector == null) { 
     // Log.e(TAG, "!!!! Bitmap Vector is NULL!!"); 
     return; 
    } 
    boolean foundOne = false; 
    for(int i = 0; i < mBitmapVector.size(); i++) { 
     Bitmap bm0 = mBitmapVector.get(i).bm; 
     if(!bm0.isRecycled()) { 
      foundOne = true; 
      break; 
     } 
    } 
    if(mBitmapVector.size() > 0 && foundOne) { 
     Log.e(TAG, " ========= Dumping Bitmap Vector === (Found a leaker) ===== "); 
     Log.e(TAG, "   "+mBitmapVector.size()+" entries"); 
     for(BitmapVectorEntry b : mBitmapVector) { 
      if(!b.bm.isRecycled()) { 
       Log.e(TAG, "  ["+b.name+"] ["+b.bm+"] Recycled ["+b.bm.isRecycled()+"]"); 
      } 
     } 
     Log.e(TAG, " ========= End of Bitmap Dump ======== "); 
    } 
} 
} 

关键位是dumpBitmaps()调用(至少对于这个问题,你上面描述)。如果努力注册所有位图,那么dumpBitmaps()调用将暴露任何需要扫描的位图。如果你不在乎其中泄漏,只是想让它消失,那么你可以拨打flush()这将删除所有的位图。

无论您在哪里创建位图,您都需要将registerBitmap()。我曾与充气做不可预知的事情可怕的运气,所以我更喜欢这样的:

public Drawable getPreformattedFile() { 
    // Log.d(TAG, "Loading in Drawable ["+preformattedFileName()+"]"); 
    if(preformattedFileName() == null) { 
     Log.e(TAG, "Formatted Filename is null"); 
     return(null); 
    } 

    Drawable ret = null; 
    try { 

     BitmapFactory.Options opts = new BitmapFactory.Options(); 
     opts.inScaled = false; 
     opts.inPurgeable = true; 

     Bitmap bm = BitmapFactory.decodeFile(preformattedFileName(), opts); 
     if(bm == null) { 
      return(null); 
     } 
     mBitmapManager.registerBitmap(mItem.name(), bm); 
     ret = new BitmapDrawable(mContext.getResources(), bm); 

     // Log.i(TAG, "   ~~~~~~~~~~~~~~~~~ JUST CHECKING ["+bm+"] ["+((BitmapDrawable) ret).getBitmap()+"]"); 

    } catch(OutOfMemoryError e) { 
     // Log.e(TAG, " ============== Before gc ==== OOME Thread ["+Thread.currentThread().getName()+"] getPreformattedFile.Before GC Heap Available [[[ "+(Debug.getNativeHeapFreeSize()/1024)+"k ]]]"); 
     System.gc(); 
     // Log.e(TAG, " ============== After gc ==== OOME Thread ["+Thread.currentThread().getName()+"] getPreformattedFile.Before GC Heap Available [[[ "+(Debug.getNativeHeapFreeSize()/1024)+"k ]]]"); 
     e.printStackTrace(); 
    } catch(Exception e) { 
     Log.e(TAG, "Trouble reading PNG file ["+e+"]"); 
    } 
    return(ret); 
} 

我已经提到几次异步做的事情,和你提到你是新十岁上下到Android。为了完整起见,我应该提到我也不喜欢使用AsyncTask,因为它对多线程有一些相当大的限制,并且图像往往需要大量的多线程。所以,相反我使用Executor,做这样的事情(这,你会注意到,所用的方法上面做实际的工作):

public Drawable getPreformattedFileAsync() { 
    if(mItem == null) { 
     Log.e(TAG, " -- ITEM is NULL!!"); 
     return(mErrorDrawable); 
    } 
    if(mFetchFileTask == null) { 
     Log.e(TAG, " -- Task is Null!!, Need to start an executor"); 
     return(mErrorDrawable); 
    } 
    Runnable job = new Runnable() { 
     public void run() { 
      Thread.currentThread().setName("ImagePipeline"); 
      Thread.currentThread().setPriority(Thread.MIN_PRIORITY); 
      Thread.currentThread().yield(); 
      if(mItemDelegate != null) { 
       Drawable retDrawable = getPreformattedFile(); 
       if(showAllDebugInformation) { 
        Log.w(TAG, " ^^^^ Getting preformatted file size ["+retDrawable.getIntrinsicWidth()+"] x ["+retDrawable.getIntrinsicHeight()+"]"); 
       } 
       if(retDrawable != null) { 
        Bitmap bm = ((BitmapDrawable) retDrawable).getBitmap(); 
        // Log.w(TAG, "  Size of Bitmap is ["+(bm.getRowBytes()*bm.getHeight())+"]"); 
        mItemDelegate.onDrawableRequest(mItem, retDrawable); 
        if(mBitmapManager != null) { 
         if(mBusyDrawable != null) { 
          mBitmapManager.recycleBitmap(((BitmapDrawable) mBusyDrawable).getBitmap()); 
         } 
         if(mErrorDrawable != null) { 
          mBitmapManager.recycleBitmap(((BitmapDrawable) mErrorDrawable).getBitmap()); 
         } 
        } 
       } else { 
        mItemDelegate.onDrawableRequest(mItem, mErrorDrawable); 
       } 
      } 
      // Log.i(TAG, " RUNNABLE - Set the background"); 
     } 
    }; 
    mImagePipelineTask.execute(job); 
    return(mBusyDrawable); 
} 

这当然需要一个执行人:

private ExecutorService mImagePipelineTask = null; 

其因此被创建:

mImagePipelineTask = Executors.newSingleThreadExecutor(); 

(或者,如果你喜欢冒险,你可以使用多线程的执行者,同样的总体思路)。

也许这有助于澄清。

+0

感谢您的支持!指向recycle()是我需要解决ViewPager内存泄漏的问题。 – Cephron

6

答案有点迟,但实际上问题很简单,我没有看到其他答案。

的是,当你手动解码位图(如你在那些注释掉行办),你必须.recycle()他们自己。所以,回到你的代码,在您的适配器,你必须补充这一点:

@Override 
public Object instantiateItem(View collection, int position) { 
    // ... 
    View view = inflater.inflate(resId, null); 
    ImageView imageView = (ImageView) view.findViewById(R.id.imageView); 
    imageView.setImageBitmap(BitmapFactory.decodeResource(getResources(), 
      ids[position])); 
    ((ViewPager) collection).addView(view, 0); 
    return view; 
} 

有了这个 - 回收加上Bitmap从层次去除View

@Override 
public void destroyItem(View collection, int position, Object o) { 
    View view = (View)o; 
    ImageView imgView = (ImageView) view.findViewById(R.id.imageView); 
    BitmapDrawable bmpDrawable = (BitmapDrawable) imgView.getDrawable(); 
    if (bmpDrawable != null && bmpDrawable.getBitmap() != null) { 
      // This is the important part 
      bmpDrawable.getBitmap().recycle(); 
    } 
    ((ViewPager) collection).removeView(view); 
    view = null; 
} 

就这么简单那就不需要使用单独的位图管理或任何东西。

+0

我得到java.lang.RuntimeException:画布:尝试使用回收位图[email protected]时,返回到以前查看的图像(现在被回收)。 – Yar

+0

@Yar这很奇怪。事情是,'destroyItem'被调用后,它用来包含的图像视图不再被渲染。因此,除了寻呼机适配器外,您还必须拥有其他一些尝试访问该位图的逻辑。如果你提供更多的细节(至少是行号和相关的代码),我们可以想出一些办法 –

+0

你好,我在获取同样的错误'java.lang.RuntimeException:Canvas:尝试使用循环位图'时滑过图像在查看寻呼机。能否请你帮忙。 – Dory