2012-05-18 45 views
1

据我所知,当我旋转屏幕时,活动被破坏并重新创建。在以下情况下,看起来情况并非总是如此。所以问题是:它真的被摧毁了吗?我出于某种原因泄漏了内存?如果它未被销毁,它是否与新创建的活动在相同的线程上运行?IntentService防止活动破坏

该场景是启动IntentService的“垂直”活动,该服务需要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); 
    } 
} 

当活动开始的第一时间,mTestVar为0,的onCreate它inceased 1。当活动被破坏时,它会保存该值。下一个活动加载它并将其增加到2,依此类推。

按照下列步骤操作:

  1. 单击Button:mTestVar是1
  2. 点击Button2的:服务得到启动
  3. 旋转屏幕5秒流逝
  4. 单击Button:mTestVar是2(的onCreate被称为)
  5. 等待5秒钟过去:mTestVar显示为仍然具有值1

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

谢谢!

编辑

看到了这个问题的答案后,我用MAT和堆转储,并发现第一个活动是永远不会被垃圾收集,即使该服务的时间了,垃圾收集和有不再提及该活动。我不知道为什么。

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

回答

3

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

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

附注:当检查变量和通用日志记录时,最好使用Log.v/e/i等。你可以在命令行用adb logcat mytag过滤它们:V *:S或使用建立的logcat进入日食。

+0

因此,当IntentService完成并被销毁时,GC应该能够销毁先前的活动。我会做更多的测试。 – cdriver

+0

使用'this.hashCode();'获取您的活动的唯一实例。 – Micro