1

据我所知,当我旋转屏幕时,活动被破坏并重新创建。在以下场景中,情况似乎并非总是如此。所以问题是:它真的被摧毁了吗?我是否出于某种原因泄漏内存?如果它没有被销毁,它是否与新创建的活动在同一个线程上运行?

该场景是一个启动 IntentService 的“垂直”Activity,该服务需要 5 秒才能完成,并使用 ResultReceiver 发回结果。在这 5 秒内,屏幕旋转,并创建了一个新的“水平”活动。结果回到“垂直”活动,而不是新的“水平”活动。那么,“垂直”活动没有被破坏吗?

我创建了一个项目来证明这一点。代码来了。

首先,带有活动布局的xml。只有两个按钮。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Show value of variable" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start IntentService" />

</LinearLayout>

之后,我们的 IntentService

public class SomeService extends IntentService {

    public SomeService(){
        super("SomeService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra("receiver");

        long t0, t1;
        t0 = System.currentTimeMillis();
        do {
            t1 = System.currentTimeMillis();
        } while (t1-t0 < 5000);

        receiver.send(0, null);
    }
}

最后是活动

public class TestActivityDestructionActivity extends Activity {

    private int mTestVar = 0;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        if (savedInstanceState != null){
            mTestVar = savedInstanceState.getInt("tv");
        }

        mTestVar++;

        Button b1 = (Button) findViewById(R.id.button1);
        b1.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Toast.makeText(TestActivityDestructionActivity.this, "Value: " + mTestVar, Toast.LENGTH_SHORT).show();
            }
        });

        Button b2 = (Button) findViewById(R.id.button2);
        b2.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Intent intent = new Intent(TestActivityDestructionActivity.this, SomeService.class);
                intent.putExtra("receiver", new SomeReceiver(new Handler()));
                startService(intent);
                Toast.makeText(TestActivityDestructionActivity.this, "IntentService started", Toast.LENGTH_SHORT).show();
            }
        });

    }

    private class SomeReceiver extends ResultReceiver{
        public SomeReceiver(Handler handler) {
            super(handler);
        }

        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            super.onReceiveResult(resultCode, resultData);
            Toast.makeText(TestActivityDestructionActivity.this, "Receiver value: " + mTestVar, Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("tv", mTestVar);
    }
}

Activity 第一次启动时,mTestVar 为 0,onCreate 增加为 1。当 Activity 被销毁时,保存该值。下一个活动加载它并将其增加到 2,依此类推。

执行以下步骤:

  1. 单击 Button1:mTestVar 为 1
  2. 单击 Button2 :服务启动
  3. 在 5 秒前旋转屏幕
  4. 单击 Button1 : mTestVar 为 2(调用了 onCreate)
  5. 等待 5 秒过去:mTestVar 仍显示为 1

据我所知,这意味着第一个活动没有被破坏。我错过了什么吗?

谢谢!

编辑

在看到这个问题的答案后,我使用了 MAT 和堆转储,发现第一个活动永远不会被垃圾收集,即使服务早已不复存在并且垃圾被收集并且不再引用该活动。我不知道为什么。

但是,如果接收器从未创建并且从未传递给服务,则不会发生内存泄漏。第一个活动按预期销毁。无论如何,没有理由不这样做。

4

1 回答 1

3

使用代码中的断点通过调试器运行它,并在 ResultReceiver 中检查“this”的 id。它保留“this”的本地副本,该副本指向您以前的活动,其中 int mTestVar == 1。当前活动的活动是一个完全不同的对象,具有自己的 mTestVar == 2 副本。这是内存泄漏。

为了更简洁地回答您的问题:Android“破坏”了您的活动,但您在回调中保留了对活动的引用,以防止它被垃圾收集。如果您希望某些东西在多个活动中“存在”,您应该使用服务来存储该状态(请记住,服务不是一个单独的线程,不应该像一个线程一样使用)。

附注:检查变量和通用日志记录时,最好使用 Log.v/e/i 等。您可以在命令行使用 adb logcat mytag:V *:S 过滤它们或使用 eclipse 中内置的 logcat。

于 2012-05-18T18:26:33.210 回答