Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why onDestroy inActivity A not being called after a call to finish() in ActivityB

The next process is simple to understand and to reproduce but leads to a bug:

  • activityA starts an activityB in its onCreate() method
  • activityB is created and I call finish() in its onResume() method
  • activityB onDestroy() is called
  • activityA onResume() is called
  • and here in activityA, I click a menu button to call finish() - or press the back key.
  • activityA is removed but onDestroy() is NOT called and A is still living ( adb shell dumpsys 'myPackageName' indicates too many living Activities )

Code

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="gleroy.com.algo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".activity.MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


        <activity
            android:name="gleroy.com.algo.activity.FakeA"
            android:label="@string/app_name"></activity>

        <activity
            android:name="gleroy.com.algo.activity.FakeB"
            android:label="@string/app_name"></activity>
    </application>
</manifest>

Activity A :

public class FakeA extends Activity {

    private final static String TAG = FakeA.class.getCanonicalName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate, taskId :" + getTaskId());
        super.onCreate(savedInstanceState);

        Intent intent = new Intent(FakeA.this, FakeB.class);
        startActivity(intent);
    }

    @Override
    protected void onResume() {
        Log.d(TAG, "onResume");
        super.onResume();
    }


    @Override
    protected void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        super.onCreateOptionsMenu(menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case R.id.stop_session_menu_item:
                /* stop and quit */
                finish();
                return false;
        }

        return super.onOptionsItemSelected(item);
    }
}

Activity B :

public class FakeB extends Activity {

    private final static String TAG = FakeB.class.getCanonicalName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate, taskId :"+getTaskId());
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume, isFinishing :" + isFinishing());
        finish();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }
}

Activity A is started from MainActivity which contains a simple button :

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MainActivity.this, FakeA.class);
            startActivity(intent);
        }
    });

So I know that we can't be sure that onDestroy() is gonna be called but here my ActivityA is clearly leaking.

Also I observed that if I use a Timer and TimerTask to delay startActivity in ActivityA or finish() in ActivityB then I don't have this bug anymore.

Here are the events :

  • FakeA onCreate, taskId :154
  • FakeA onResume
  • FakeA onPause, isFinishing : false
  • FakeB onCreate, taskId :154
  • FakeB onResume, isFinishing :false
  • FakeA onResume
  • FakeB onDestroy
  • call finish or press back key : FakeA onPause, isFinishing : true
like image 448
gleroyDroid Avatar asked Apr 29 '15 11:04

gleroyDroid


People also ask

Does onDestroy finish call?

finish() usually triggers a call to onDestroy() . Generally speaking, finish() will eventually result in onDestroy() being called.

Can the system destroy an activity without calling onDestroy?

Can the system destroy an activity without calling onDestroy? You don't need to call stop( ) method. Android system automatically go thru those life cycle methods.

When onDestroy is called in Android?

onDestroy( ) is called before the activity is destroyed. The system invokes this callback either because: the activity is finishing (due to the user completely dismissing the activity or due to finish( ) being called on the activity), or.

When onResume method is called in Android?

onResume() is called whenever you navigate back to the activity from a call or something else. You can override the onResume method similarly as onCreate() and perform the task. This may help you understand the lifecycle of and Android app more.


1 Answers

In place of finish() try finishAffinity(). As far as i know: finish() just destroys the current live Activity while, finishAffinity() destroys all active Activities.

like image 191
visnkmr Avatar answered Nov 15 '22 00:11

visnkmr