Search code examples
javaandroidandroid-activityandroid-lifecycleonresume

Why previous Activity get destroyed pressing up button on Setting Activity Toolbar?


GOAL: when the user press back on SettingsActivity toolbar the previous Activity should be resumed from where the user left.

PROBLEM: From Activity lifecycle reported on android developer website I understand that the previous Activity should be resumed calling OnResume method, instead in my case the MainActivity start again calling OnCreate method.

In particular the flow is as follows:

1) User click on icon to start SettingsActivity

2)MapsActivity invokes onPause, then onSaveInstanceState, then onStop

3) User click back button on SettingsActivity

4)MapsActivity invokes onDestroy then onCreate (and everything I tried to save during point 2 with saveInstanceState is lost because Bundle is always null)

CODE

MapsActivity (main activity)

 @Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_maps);

    // .... other initialization code ... //

    // If location permission is granted initialize Map
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {

        mapSync();

    } else {


        requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSION_FINE_LOCATION);

    }

}

@Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean("prova", true);
    }


// Where I start the second activity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection
    switch (item.getItemId()) {
        case R.id.connection_type:
              // .... handle this case ... //
case R.id.settings:

                Intent intent = new Intent(MapsActivity.this, SettingsActivity.class);
                startActivity(intent);
                return true;

            default:
                return super.onOptionsItemSelected(item);
        }
    }

SettingsActivity (called activity)

public class SettingsActivity extends AppCompatActivity
{

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

        // Set the toolbar
        Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);

        myToolbar.setTitle("Settings");
        myToolbar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24px);
        myToolbar.setNavigationOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {

                finish();
            }
        });
        setSupportActionBar(myToolbar);



    }


    public  static class MainSettingsFragment extends PreferenceFragmentCompat {

        public final static String KEY_ENABLE_BACKGROUND_UPDATE = "enable_background_update";
        public final static String KEY_ENABLE_LAST_KNOWN_LOCATION = "enable_last_known_location";
        public final static String KEY_DELETE_DB_DATA = "delete_db_data";
        public final static String KEY_CHANGE_MAP_COLOR ="change_map_color";

        private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;
        @Override
        public void onCreatePreferences(Bundle bundle, String s) {
            // Load the Preferences from the XML file
            addPreferencesFromResource(R.xml.preferences);

            preferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
                @Override
                public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

                    if(key.equals(KEY_DELETE_DB_DATA))
                    {

                        String connectivityType = sharedPreferences.getString(key, null);
                        new DeleteAreaAsync(SignalAreaDatabase.getDatabase(getContext()), connectivityType).execute();

                    } else if(key.equals(KEY_CHANGE_MAP_COLOR)){

                        String gradientColor = sharedPreferences.getString(key, null);
                        SignalAreaDrawer signalAreaDrawer = SignalAreaDrawer.getSignalAreaDrawer();
                        signalAreaDrawer.setGradientColor( gradientColor);





                    }

                }
            };

        }

        @Override
        public void onResume() {
            super.onResume();
            getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(preferenceChangeListener);
        }

        @Override
        public void onPause() {
            super.onPause();
            getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(preferenceChangeListener);
        }
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>

<!--
     The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
     Google Maps Android API v2, but you must specify either coarse or fine
     location permissions for the 'MyLocation' functionality. 
-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Less accurate location: telephony manager and location requests -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Access to wifi network information -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- Change wifi connectivity state -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.AppCompat.Light.NoActionBar">

    <!--
         The API key for Google Maps-based APIs is defined as a string resource.
         (See the file "res/values/google_maps_api.xml").
         Note that the API key is linked to the encryption key used to sign the APK.
         You need a different API key for each encryption key, including the release key that is used to
         sign the APK for publishing.
         You can define the keys for the debug and release targets in src/debug/ and src/release/. 
    -->
    <meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="@string/google_maps_key" />

    <activity
        android:name=".MapsActivity"
        android:label="@string/title_activity_maps">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name=".SettingsActivity"
        android:parentActivityName=".MapsActivity"
        android:theme="@style/PreferenceScreen" />
</application>

QUESTION: how can I restore the previous Activity state and show to the user exactly what he was visualizing when he opened the second Activity?

EDIT #1: I tried to override oonDestroy() , onSaveInstance(), onRestoreInstance() and this is what happens:

  • when I start the setting activity, the main activity go onPause() as I would expect.

  • when I press the back button on the settings activity , the main activity go before onDestroy() and immediately after onCreate(), not calling onSaveInstance() or onRestoreInstance() at all.

EDIT #2: the app didn't go through onSaveInstanceState(Bundle outState) probably because I declared it public. Now the app calls it. So I tried to save some info like outState.putBoolean("prova", true); but when the mainActivity is destroyed , at new onCreate(Bundle savedInstanceState) invokation the Bundle savedInstanceState is always null.

EDIT #3: as @SherifelKhatib suggested I tried to delete all finish() statement from MapsActivity, and I tried to substitute MapsActivity with a minimal EmptyActivity to see if the problem was in MapsActivity. Unfortunately the app has the same behaviour. When the user press the back button the previous app is always destroyed. No way to restore its state.

EDIT #4: what I tried and still doesn't work. Modifying SettingsActivity :

First approach

 @Override
    public void onBackPressed(){
        moveTaskToBack(true);
    }

Second approach

getSupportActionBar().setDisplayHomeAsUpEnabled(true);

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    // Respond to the action bar's Up/Home button
    case android.R.id.home:
        NavUtils.navigateUpFromSameTask(this);
        return true;
    }
    return super.onOptionsItemSelected(item);
}

Third approach

@Override
public void onBackPressed() {
    Intent backIntent = new Intent(this, MapsActivity.class);
    backIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
    startActivity(backIntent);
}

And add this in MainActivity:

 Intent intent = new Intent(MapsActivity.this, SettingsActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
                startActivity(intent);

Solution

  • SOLUTION:

    Add this attribute to MainActivity in the manifest .

    android:launchMode="singleTop"
    

    Explanation

    The "standard" and "singleTop" modes differ from each other in just one respect: Every time there's a new intent for a "standard" activity, a new instance of the class is created to respond to that intent. Each instance handles a single intent. Similarly, a new instance of a "singleTop" activity may also be created to handle a new intent. However, if the target task already has an existing instance of the activity at the top of its stack, that instance will receive the new intent (in an onNewIntent() call); a new instance is not created.