在OnDestroy中回收位图 - 重新启动活动时出现“尝试使用已回收的位图”

5
我有两个简单的活动 AB。用户通过按下按钮从A开始B,然后通过按返回按钮返回到A
B活动的onDestroy()方法中,我回收了一些在B活动中使用的背景图像。我的疑问是:为什么当再次启动B活动时,我会得到“尝试使用已回收位图”的错误?难道不应该在onCreate()方法中再次加载位图吗?就像在第一次启动活动时必须加载位图一样。
以下是我的示例代码:
public class ActivityB extends Activity {

     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);    
         setContentView(R.layout.selectionpage);    
     }  

     @Override
     public void onDestroy() {      
        ImageView iv = (ImageView) findViewById(R.id.imageView1);
        ((BitmapDrawable)iv.getDrawable()).getBitmap().recycle();
        LinearLayout ll = (LinearLayout) findViewById(R.id.linearLayout1);
        ((BitmapDrawable)ll.getBackground()).getBitmap().recycle();
        super.onDestroy();
     }
 }

我在从 A 启动 B 活动时使用的代码

     Intent intent = new Intent(ActivityA.this, ActivityB.class);
     startActivity(intent);

selectionpage.XML :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/linearLayout1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@drawable/backgroundimage">

        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/selectionimage"/>
        </LinearLayout>                

这部分内容可能是相关的。我不确定。我注意到在启动活动B之后,即使它已经被销毁,在使用MAT分析内存堆时仍然可以看到我的活动实例。GC根路径似乎通过Java.lang.Thread和ContextImpl。

3个回答

2

您会收到该错误,因为ImageView ivLinearLayout ll仍指向已回收的位图。您不需要在onDestroy()内部自行进行回收。当系统不再需要位图时,它们将被释放。


1
是的。我是个白痴——我没意识到xml文件是这样工作的。不过我注意到我的程序的另一个区域在加载几个大图像后似乎会出现内存不足错误。即使有些图像不再使用,GC似乎也不想删除它们。我不得不调用recycle()来通知GC立即清理它们,而不是等待一段时间。无论如何,我在胡言乱语。谢谢你的回答。 - Wozza
1
这并不适用于所有版本的Android: “在Android 2.3.3(API级别10)及更低版本中,位图的后备像素数据存储在本地内存中。它与位图本身分开存储在Dalvik堆中。本地内存中的像素数据没有以可预测的方式释放,可能导致应用程序短暂超出其内存限制并崩溃。” http://developer.android.com/training/displaying-bitmaps/manage-memory.html#inBitmap - ForceMagic

1

如果您在单独的线程中引用了活动并使其保留时间超过应有的时间,则可能会出现内存泄漏。

这可能会导致旧活动中的iv和ll在它们被回收后仍在使用位图。您可以执行iv.setImageDrawable(null)和ll.setBackgroundDrawable(null),但这些位图是由系统创建的,您不应该需要回收它们。

我猜想您试图回收它们是因为遇到了内存问题?这可能更好地解释了上述可能的泄漏。


我确实有内存问题,你说得对。我已经四处搜索,找不到任何可能导致此活动具有内存泄漏的参考资料。我试图进行回收,因为我无法理解为什么当我启动B活动时,会失去3Mb的堆空间。当我关闭活动时,似乎没有得到它。当我重新启动活动时,堆只增加了几个kbs。就像我说的,我已经研究了内存转储,并可以看到该活动似乎仍然存在,但无论我多少次停止/启动该活动,它仍然是唯一的副本。 - Wozza
你在B中做了什么 - 你有任何线程或异步任务吗?你有任何内部类吗?你设置了像drawable或view这样的静态变量吗?你保留了活动实例吗? - FunkTheMonk
我认为我在B中遇到的问题与AdMob有关(我有一个横幅广告)。我尝试删除AdMob,一切似乎都清除得很好。我认为AdMob正在占用我的上下文并且没有销毁自己,从而阻止其他任何东西被清理。 - Wozza

-1

你必须在代码中完成它。 在代码中初始化Bitmap对象并使用ImageView.setImageBitmap(bitmap)。

Bitmap bitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.selectionpage);   
     bitmap = new BitmapFactory.decode...
     yourImageView.setImageBitmap(bitmap);

 }  
 @Override
 public void onDestroy() {      

    super.onDestroy();
    // do recycle bitmap here
    bitmap.recycle();
 }

这段代码很可能会引发以下错误:FATAL EXCEPTION: java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@410a9720 which means recycle was called too early while a canvas was still using it. - ForceMagic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接