安卓ViewPager与图像:内存泄漏/应用程序崩溃



我正在编写一个应用程序,该应用程序显示全景图片,最终在其上有几个标记,以显示有关某些点的信息。

由于大图像导致应用程序崩溃(我在应用程序中还有另一个显示大地图的活动),我现在正试图用ViewPager将全景显示为一系列页面。

我已经设法用6位显示了这张照片,我认为一切都很顺利,但现在由于内存耗尽,该应用程序在刷了几次(大约7到8次)后就崩溃了。

我拔头发,为什么我认为我的物品一旦离开屏幕就会被销毁??我是个十足的傻瓜,如果我浪费时间,我很抱歉。我花了一整天的时间阅读和尝试这里和其他地方的解决方案,但我一点也不知道。

这是我的代码:活动全景查看

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的图像。-我在这个活动中显示的六个图像(全景视图)在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>

我试过设置屏幕外的页面限制,但没有帮助。

我在另一篇帖子上找到了这个关于内存管理的链接,今晚我会看一看。

编辑:这是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以下。

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

问题是,当您手动解码位图(就像您在注释行中所做的那样)时,您必须自己.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;
}

有了这个——除了从层次结构中删除View之外,还回收Bitmap

@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;
}

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

有充分的文档记录的问题,尽管您似乎已经解决了它,但这里有一些潜在的有用信息。

从你的日志,我发现你正在开发(或至少测试)姜饼(因为Honeycomb以后不包括"外部"部分输出。这一点之所以重要,只是因为它突出显示了位图中发生的事情。在Gingerbread中,位图数据被放置在堆外的本机内存中。然后,指向该数据的指针与其他一些引用信息一起放置在堆中。删除位图的每个引用将释放引用信息(在某个时刻,当System.gc()运行时)。但是,位图数据永远不会被释放,除非您的设备被小行星撞击——或者(不那么引人注目)您在该位图上调用recycle()方法。应该注意的是,这个调用确实释放了本地内存,因此,当ViewPager(很可能在instantiateItem中)再次需要位图时,您确实需要从头开始重新创建位图。蜂窝式并向前将位图数据放置在堆栈中,这稍微不那么烦人。只有当您recycle()该位图时,data才会释放(所以你必须更深入地研究,使用DDMS来确定发生了什么——例如,我目前正在JB上解决同样的问题,在ICS中,4.0.3的行为与4.0.4不同,但我跑题了)。

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

这是我用来跟踪事物的(有点行人的)类,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);
}

我已经提到过几次异步做事,你也提到你是安卓系统的新手。为了完整起见,我应该提到,我也不喜欢使用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();

(或者,若你们有冒险精神,你们可以使用一个多线程执行器,和一般想法一样)。

也许这有助于澄清。

所以使用

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

好运

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

这里CCD_ 17是可以保留在Visibility之外的页数。任何额外的东西都会被销毁,只有当用户滑回附近的位置时才能重新创建。

最新更新