1

私の知る限り、画面を回転させると、アクティビティが破棄されて再作成されます。次のシナリオでは、常にそうであるとは限りません。問題は、それは本当に破壊されているのかということです。何らかの理由でメモリリークしていますか?破棄されていない場合、新しく作成されたアクティビティと同じスレッドで実行されますか?

このシナリオは、IntentService を開始する「垂直」アクティビティです。サービスが完了するまでに 5 秒かかり、ResultReceiver を使用して結果を返します。この 5 秒間で画面が回転し、新しい「水平」アクティビティが作成されます。結果は、新しい「水平」アクティビティではなく、「垂直」アクティビティに戻ります。それで、「垂直」活動は破壊されませんか?

これを実証するプロジェクトを作成しました。これがコードです。

まず、アクティビティ レイアウトを含む xml。ボタンはたったの2つ。

<?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);
    }
}

アクティビティが最初に開始されるとき、mTestVar は 0 であり、onCreate は 1 に増加します。アクティビティが破棄されると、値が保存されます。次のアクティビティはそれをロードして 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 を確認します。int mTestVar == 1 である以前のアクティビティを指す「this」のローカル コピーを保持しています。現在アクティブなアクティビティは、mTestVar == 2 の独自のコピーを持つ、まったく異なるオブジェクトです。これはメモリ リークです。

質問にもっと簡潔に答えるには: Android はあなたのアクティビティを「破壊」しましたが、コールバックでアクティビティへの参照を保持し、ガベージ コレクションが行われないようにしました。何かを複数のアクティビティにまたがって「存続」させたい場合は、サービスを使用してその状態を保存する必要があります (サービスは個別のスレッドではなく、スレッドのように使用しないでください)。

補足: 変数と汎用ログをチェックするときは、Log.v/e/i などを使用するのが最善です。コマンド ラインで adb logcat mytag:V *:S を使用してそれらをフィルタリングするか、Eclipse に組み込まれている logcat を使用します。

于 2012-05-18T18:26:33.210 に答える