带有图像的Android ViewPager:内存泄漏/应用程序崩溃

时间:2012-11-27 14:58:44

标签: android memory-leaks 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。

4 个答案:

答案 0 :(得分:8)

答案有点迟了,但问题实际上很简单,而且我没有看到针对它的其他答案。

事情是,当你手动解码位图时(正如你在那些注释掉的行中那样),你必须 .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;
}

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

答案 1 :(得分:4)

记录良好的问题,即使你似乎已经解决了,但这里有一些潜在的有用信息。

从你的logcat,我发现你正在开发(或至少测试)Gingerbread(因为Honeycomb以后不包括输出中的“外部”部分。这很重要,因为它突出显示了你的位图发生了什么在Gingerbread中,位图数据被放置在堆外的本机内存中。然后,该数据的指针被放置在堆中,以及一些其他参考信息删除对位图的每个引用都会释放引用信息(在某些时候运行System.gc()时)。但是,位图数据将永远不会被释放,除非你的设备被小行星击中 - 或者(不那么戏剧性)你在那个位图上调用recycle()方法。应该注意的是,这个调用确实释放了本机内存,所以你真的需要重新启动 - ViewPager再次需要时,从头开始创建位图(最有可能在instantiateItem。Honeycomb及以后将位图数据放在堆栈中,这稍微不那么烦人了。 data仅在recycle()位图时才会释放(因此您需要更深入地挖掘,使用DDMS来确定正在发生的事情 - 例如,我是目前在JB上遇到同样的问题,在ICS中,4.0.3的行为与4.0.4不同,但我离题了。

这可能有些过分,但我的解决方案是实现一个类来跟踪我的位图,并确定我已经回收了它们 - 在你的情况下,当你在ViewPager中destroyItem()时会发生这种情况。 / p>

这是我用来追踪事物的(有点行人)课程,fwiw。

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

(或者,如果你喜欢冒险,你可以使用多线程执行者,相同的一般想法)。

也许这有助于澄清。

答案 2 :(得分:3)

所以使用

1

@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

古德勒克

答案 3 :(得分:2)

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

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