How can I return to a parent activity correctly?
Asked Answered
C

16

189

I have 2 activities (A and B) in my android application and I use an intent to get from activity A to activity B. The use of parent_activity is enabled:

 <activity
        android:name=".B"
        android:label="B" >
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.app_name.A" />
  </activity>

I also use a theme which provides an UP-button.

So after I called activity B I can use the UP-button to get back to the activity A. The problem is that the application seems to call the onCreate()-function of activity A again and this is not the behaviour I need. I need activity A to look the same way like it looked before I called activity B.

Is there a way to achieve this?

EDIT

I didn't write any code to start activity B from activity A. I think it is auto-generated by Eclipse.

Class B looks like:

    @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_b);
    getActionBar().setDisplayHomeAsUpEnabled(true);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_b, menu);
    return true;
}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            NavUtils.navigateUpFromSameTask(this);
            return true;
    }
    return super.onOptionsItemSelected(item);
}
Crave answered 5/9, 2012 at 6:55 Comment(7)
Post you code, for starting Activity A from B..Aryan
If I understand you right, you can use startActivityForResult() and return a resultCode or something.Piccalilli
Please update your tagged correct answer! The CORRECT answer is coming from LorenzCK - not from user......! Tagging this as correct is misleading and makes even more programmers misunderstand up navigation as opposed to back navigation!Bastardy
Gee, so many wrong answers here, could you please help in cleaning this up...?Bastardy
@Crave - Correct answer as per your code design is updated.Aryan
Note that NavUtils.navigateUpFromSameTask(this); will work correctly only for API level 15 and lower. For higher API levels, it recreates the Parent Activity. This is explained beautifully here, https://mcmap.net/q/136986/-up-navigation-broken-on-jellybeanPrisage
Try this solution in the child activity: https://mcmap.net/q/136987/-android-refresh-parent-activity-when-child-activity-finishesStorytelling
C
349

You declared activity A with the standard launchMode in the Android manifest. According to the documentation, that means the following:

The system always creates a new instance of the activity in the target task and routes the intent to it.

Therefore, the system is forced to recreate activity A (i.e. calling onCreate) even if the task stack is handled correctly.

To fix this problem you need to change the manifest, adding the following attribute to the A activity declaration:

android:launchMode="singleTop"

Note: calling finish() (as suggested as solution before) works only when you are completely sure that the activity B instance you are terminating lives on top of an instance of activity A. In more complex workflows (for instance, launching activity B from a notification) this might not be the case and you have to correctly launch activity A from B.

Cerell answered 10/4, 2013 at 18:43 Comment(2)
Here is the explanation from Android docs, 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.Prisage
Note that according to the documentation navigateUpFromSameTask(...) should be adding FLAG_ACTIVITY_CLEAR_TOP to upIntent (via navigateUpTo(...) which should result in same behavior (according to this guide), but the flag is never set - and therefore this answer makes sensePercussion
A
83

Updated Answer: Up Navigation Design

You have to declare which activity is the appropriate parent for each activity. Doing so allows the system to facilitate navigation patterns such as Up because the system can determine the logical parent activity from the manifest file.

So for that you have to declare your parent Activity in tag Activity with attribute

android:parentActivityName

Like,

<!-- The main/home activity (it has no parent activity) -->
    <activity
        android:name="com.example.app_name.A" ...>
        ...
    </activity>
    <!-- A child of the main activity -->
    <activity
        android:name=".B"
        android:label="B"
        android:parentActivityName="com.example.app_name.A" >
        <!-- Parent activity meta-data to support 4.0 and lower -->
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.app_name.A" />
    </activity>

With the parent activity declared this way, you can navigate Up to the appropriate parent like below,

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

So When you call NavUtils.navigateUpFromSameTask(this); this method, it finishes the current activity and starts (or resumes) the appropriate parent activity. If the target parent activity is in the task's back stack, it is brought forward as defined by FLAG_ACTIVITY_CLEAR_TOP.

And to display Up button you have to declare setDisplayHomeAsUpEnabled():

@Override
public void onCreate(Bundle savedInstanceState) {
    ...
    getActionBar().setDisplayHomeAsUpEnabled(true);
}

Old Answer: (Without Up Navigation, default Back Navigation)

It happen only if you are starting Activity A again from Activity B.

Using startActivity().

Instead of this from Activity A start Activity B using startActivityForResult() and override onActivtyResult() in Activity A.

Now in Activity B just call finish() on button Up. So now you directed to Activity A's onActivityResult() without creating of Activity A again..

Aryan answered 5/9, 2012 at 7:0 Comment(6)
I don' know where startActivity() in activity B is called. I've posted the source code of activity B...Crave
Instead of code line NavUtils.navigateUpFromSameTask(this); write finish() .Aryan
It begs the question, how come Google doesn't mention anything about finish() or startActivityForResult() in their documentation on Navigation (developer.android.com/design/patterns/navigation.html) ?Aleph
this sounds like a laborious workaround. @Cerell answer seems to be much better.Ivanovo
Thanks a lot for using NavUtils.navigateUpFromSameTask(this). Big plus! I have been looking for a method like this to replace finish() which does not return to the parent activity under some circumstances. finish() is essentially going back to the previous activity that is not necessarily the parent activity.Reclusion
Note that the AppCompatActivity class now has a onSupportNavigateUp method which handles everything. It should be better to use that instead of the NavUtils methods.Bullnecked
S
24

I had pretty much the same setup leading to the same unwanted behaviour. For me this worked: adding the following attribute to an activity A in the Manifest.xml of my app:

android:launchMode="singleTask"

See this article for more explanation.

Shanel answered 17/3, 2013 at 10:29 Comment(2)
From my point of view the correct solution to the problem. This will behave just like if you called finish() if the Activity exists in the task stack. If that is not the case it will be created and started.Jessikajessup
This is not the correct way as it will create a new task unnecessarily every time, you should instead use the launchMode="singleTop".Prisage
B
20

Although an old question, here is another (imho the cleanest and best) solution as all the previous answeres didn't work for me since I deeplinked Activity B from a Widget.

public void navigateUp() {
final Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot()) {
    Log.v(logTag, "Recreate back stack");
        TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent).startActivities();
  } else {
    NavUtils.navigateUpTo(this, upIntent);
  }
}

[https://mcmap.net/q/136988/-navutils-navigateupto-does-not-start-any-activity ]

But also see: https://speakerdeck.com/jgilfelt/this-way-up-implementing-effective-navigation-on-android

Bouillon answered 13/2, 2016 at 16:13 Comment(0)
O
7

A better way to achieve this is by using two things: call:

NavUtils.navigateUpFromSameTask(this);

Now, in order for this to work, you need to have your manifest file state that activity A has a parent activity B. The parent activity doesn't need anything. In version 4 and above you will get a nice back arrow with no additional effort (this can be done on lower versions as well with a little code, I'll put it below) You can set this data in the manifest->application tab in the GUI (scroll down to the parent activity name, and put it by hand)

Support node:

if you wish to support version below version 4, you need to include metadata as well. right click on the activity, add->meta data, name =android.support.PARENT_ACTIVITY and value = your.full.activity.name

to get the nice arrow in lower versions as well:

getSupportActionBar().setDisplayHomeAsUpEnabled(true);

please note you will need support library version 7 to get this all working, but it is well worth it!

Orchidaceous answered 22/9, 2013 at 14:50 Comment(0)
A
6

What worked for me was adding:

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case android.R.id.home:
                onBackPressed();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onBackPressed() {
        finish();
    }

to TheRelevantActivity.java and now it is working as expected

and yeah don't forget to add:

getSupportActionbar.setDisplayHomeAsUpEnabled(true); in onCreate() method

Arabia answered 28/7, 2017 at 10:22 Comment(0)
B
5

Adding to @LorenCK's answer, change

NavUtils.navigateUpFromSameTask(this);

to the code below if your activity can be initiated from another activity and this can become part of task started by some other app

Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
    TaskStackBuilder.create(this)
            .addNextIntentWithParentStack(upIntent)
            .startActivities();
} else {
    NavUtils.navigateUpTo(this, upIntent);
}

This will start a new task and start your Activity's parent Activity which you can define in Manifest like below of Min SDK version <= 15

<meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value="com.example.app_name.A" />

Or using parentActivityName if its > 15

Benyamin answered 22/3, 2016 at 19:13 Comment(1)
Note: Do declare parentActivityName for SDK >15 or else it'll give you some random crashesBenyamin
B
4

I tried android:launchMode="singleTask", but it didn't help. Worked for me using android:launchMode="singleInstance"

Bunion answered 4/3, 2015 at 8:20 Comment(1)
Adding android:launchMode="singleInstance" to parent activity solved problem for meAcheron
F
3

I had a similar problem using android 5.0 with a bad parent activity name

<activity
        android:name=".DisplayMessageActivity"
        android:label="@string/title_activity_display_message"
        android:parentActivityName=".MainActivity" >
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.myfirstapp.MainActivity" />
    </activity>

I removed the com.example.myfirstapp from the parent activity name and it worked properly

Frater answered 29/10, 2014 at 16:27 Comment(0)
B
3

In Java class :-

    toolbar = (Toolbar) findViewById(R.id.apptool_bar);
    setSupportActionBar(toolbar);

    getSupportActionBar().setTitle("Snapdeal");

    getSupportActionBar().setHomeButtonEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

In Manifest :-

<activity
            android:name=".SubActivity"
            android:label="@string/title_activity_sub"
            android:theme="@style/AppTheme" >
            <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity"></meta-data>
    </activity>

It will help you

Bani answered 28/5, 2016 at 7:39 Comment(0)
F
2

Add to your activity manifest information with attribute

android:launchMode="singleTask"

is working well for me

Frances answered 8/3, 2014 at 6:11 Comment(2)
Comlete flow is like that @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: NavUtils.navigateUpFromSameTask(this); return true; } return super.onOptionsItemSelected(item); }Frances
Add to your manifest <activity android:name=".MainActivity" android:label="@string/title_activity_main" > </activity> <activity android:name=".CreateEventActivity" android:label="@string/title_activity_create_event" android:launchMode="singleTask" android:parentActivityName=".MainActivity" > <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity" /> </activity>Frances
B
1

try this:

Intent intent;
@Override
public void onCreate(Bundle savedInstanceState) {
    intent = getIntent();   
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_b);
    getActionBar().setDisplayHomeAsUpEnabled(true);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_b, menu);
    return true;
}  

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            NavUtils.navigateUpTo(this,intent);
            return true;
    }
    return super.onOptionsItemSelected(item);
}
Brendon answered 29/9, 2013 at 5:43 Comment(0)
E
1
    @Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
    case android.R.id.home:
        finish();
        return true;
}
return super.onOptionsItemSelected(item);

like a Back press

Emmenagogue answered 5/8, 2017 at 10:54 Comment(0)
M
0

Going into my manifest and adding android:launchMode="singleTop" to the activity did the trick for me.

This specifically solved my issue because I didn't want Android to create a new instance of the previous activity after hitting the Up button in the toolbar - I instead wanted to use the existing instance of the prior activity when I went up the navigation hierarchy.

Reference: android:launchMode

Mortification answered 18/2, 2016 at 23:47 Comment(0)
S
0

Try this solution use NavUtils.navigateUpFromSameTask(this); in the child activity: https://mcmap.net/q/136987/-android-refresh-parent-activity-when-child-activity-finishes

Storytelling answered 23/4, 2018 at 12:29 Comment(0)
F
0

In any context, If you want's to open parent activity on onBackPressed

protected fun navigateToParent() {
        NavUtils.getParentActivityIntent(this)?.let { upIntent ->
            if (NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot) {
                Timber.d("navigateToParent: sourceActivity should recreate")
                startActivity(upIntent)
            } else {
                Timber.d("navigateToParent: sourceActivity in stack, just finish")
            }
        }
        super.onBackPressed()
    }

Set android:parentActivityName in manifest

 <activity
            android:name=".feature.signup.SignUpActivity"
            android:parentActivityName=".feature.welcome.WelcomeActivity"
            android:windowSoftInputMode="adjustResize" />
Faline answered 27/4, 2022 at 11:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.