NavUtils


#1

Hi there,

This book has been very helpful so far! Thank you.

I have a question about how NavUtils works. In Chapter 16, you mention we should use NavUtils.navigateUpFromSameTask instead of launching an intent. I have two activities, A and B, and in B, I call this method. This launches the parent activity but it also seems to clear any selected state. With the debugger launched, I have verified that my saved instance state (bundle) is null in my fragments.

I would like to have the same functionality as-if I pressed the back button, which seems to preserve the state. Do you have any advice?

Thanks!


#2

To accomplish that, you just need to tweak the intent you’re sending out a bit. To do that, you’ll need to use NavUtils a little differently:

// Get the intent that navigateUpFromSameTask would send out
Intent i = NavUtils.getParentActivityIntent(getActivity());

// Add the FLAG_ACTIVITY_SINGLE_TOP flag so that onNewIntent
// is called rather than wiping out the current instance
i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

// Fire the intent as navigateUpFromSameTask would
NavUtils.navigateUpTo(getActivity(), i);

Using FLAG_ACTIVITY_SINGLE_TOP is similar to using the singleTop launch mode, as described later on in the Search chapter.


#3

Ok great! I’ll try that out. Thank you!


#4

I cut and paste my responses in another thread here, hoping you will respond to this one:

I found the same thing. With the book code (and using FLAG_ACTIVITY_[color=#FF0000]SINGLE[/color]_TOP), I get the following in LogCat when the Android icon (the one added by us) is pressed to go back to the CrimeListFragment:

06-26 01:50:30.273: D/CrimeListFragment(1492): onDestroy() 06-26 01:50:30.273: D/CrimeListFragment(1492): onDetach() 06-26 01:50:30.515: D/CrimeListFragment(1492): onAttach() 06-26 01:50:30.515: D/CrimeListFragment(1492): onCreate() 06-26 01:50:30.523: D/CrimeListFragment(1492): onCreateView() 06-26 01:50:30.543: D/CrimeListFragment(1492): onResume() 06-26 01:50:31.143: D/CrimeListFragment(1492): onCreateOptionsMenu()

If the Android device back-button is pressed instead, or the solution by AnnRos (she used FLAG_ACTIVITY_[color=#FF0000]CLEAR[/color]_TOP):

06-26 01:54:27.452: D/CrimeListFragment(1492): onResume()

The code:

[code]@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
if (NavUtils.getParentActivityName(getActivity()) != null) {
NavUtils.navigateUpFromSameTask(getActivity()); // BOOK CODE

                    // AnnRos Code:
		//Intent i = NavUtils.getParentActivityIntent(getActivity());
		//i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
		//NavUtils.navigateUpTo(getActivity(), i);
	}
	return true;
default:
	return super.onOptionsItemSelected(item);
}

}[/code]

Is there a way to also accomplish this in the manifest?

I also found this works (only the onResume() method is called and no intent needed!):

if (NavUtils.getParentActivityName(getActivity()) != null) { getActivity().finish(); }

Thanks!


#5

This is an oversimplification in the text - under the default launch mode, a new activity instance must always be started when an intent is sent, even when the CLEAR_TOP flag is added to the intent.

I haven’t tested this out, but you should be able to fix this by changing the launch mode to singleTop in the manifest (as described in the Search chapter in the text).


#6

[quote=“phillips”]This is an oversimplification in the text - under the default launch mode, a new activity instance must always be started when an intent is sent, even when the CLEAR_TOP flag is added to the intent.

I haven’t tested this out, but you should be able to fix this by changing the launch mode to singleTop in the manifest (as described in the Search chapter in the text).[/quote]

Sorry, but … huh? in the code CLEAR_TOP works. SINGLE_TOP does not. I tried reading the API docs to figure out how you would put it in the manifest, but the docs don’t give any detail on how you do things in the XML to get the same result as the code.

FLAG_ACTIVITY_CLEAR_TOP in code is what in the XML? I think I tried this in the for CrimePagerActivity:

<intent-filter> <flag android:name="android.intent.activity.CLEAR_TOP" /> </intent-filter>

What is wrong with just calling [color=#BF0000]getActivity().finish();[/color] ?

Thanks!


#7

Dear authors,

Please don’t be offended by my questions. I would NOT be reading the book if it wasn’t evident that you really know your stuff, and I appreciate the way you have organized the presentation. I only ask questions AFTER trying to find the answer myself. I am pretty good at using google and can read through the android specs.

The use of XML files and programs that extend various android classes takes some getting used to. I have even created my own diagrams of things to try and grasp the hierarchy and understand the instruction paths.

The only thing that makes me wonder about you is when you don’t answer a question. You provided a solution, people are telling you it doesn’t work, provided an alternate solution that does, and I found a seemingly simple solution that does not require the use of an intent. What we need to hear from you now is an acknowledgment that your solution was wrong or an explanation of why it should work. I would like to know why we can’t just call the finish() method, since it works.

Even if you answer the question in future chapters, I would like to understand it now and find it hard to continue when I am still stumped by something. My brain exits learning mode when it is in question mode.

Thanks!


#8

No worries! I would have responded to your question earlier, but I’ve been a little busy. I also wanted to make sure I was clear and accurate here, because I’ve already said the wrong thing once. (yes, the earlier solution was incorrect)

It’s a good thing I did, too, because the stuff you guys found made me realize that my understanding was all wrong. See, my understanding was that FLAG_ACTIVITY_CLEAR_TOP was already being set. This is why I suggested adding the SINGLE_TOP flag - my assumption was that this would prevent the activity from being relaunched, as it does when set in the manifest.

Of course, as you can see, it does have an effect - on JellyBean. Try running the sample code on ICS or earlier and see what happens, though.

NavUtils does not behave consistently here, and the differences are not documented well. So I did a little research, and here’s what I found out:

  • In JellyBean, navigateUpFromSameTask will call Activity.navigateUpTo(Intent), which respects your activity’s launch mode. That means that if you’re using the default “standard” launch mode, it will relaunch the activity. If you’re using “singleTop” or another launch mode, it will deliver the new intent to the already-running activity.
  • In ICS and prior versions, Activity.navigateUpTo(Intent) does not exist. So to get around this limitation, NavUtils adds the CLEAR_TOP flag. This flag asks for activities over this activity to be cleared, leaving the desired activity on top. The activity will not be relaunched.

For consistent behavior, I would recommend the AnnRos solution you quote.

I would not recommend your solution, which calls Activity.finish(). It works perfectly fine for CriminalIntent, but that is only because CriminalIntent has a very simple hierarchy - hitting the back button is always the same as hitting the home button. If the home button leads to a different activity than the back button, your solution will not work. (See this site for examples of navigation flows where home is different from back: http://developer.android.com/design/patterns/navigation.html)


#9

Thanks for the great explanation! Coming from Windows programming, I am comparing how views and fragments get displayed and destroyed to how we would display a child window or a modal window. The parent controls the window. In reality, we should not see the detail view of a CrimeFragment (or CrimePagerActivity) as a child window/view of the CrimeListFragment. It’s weird that how CrimeFragment ends determines whether CrimeListFragment destroys and recreates itself or not.


#10

You’re absolutely right - Android is definitely more difficult to understand if you’re coming from another GUI framework like Windows or iOS. In those environments, you generally have complete control, but Android’s OS reserves a lot of power in how it manipulates activities.


#11

Thanks very much for this explanation here. I am now using the AnnRos Code solution too and it works well.

However, finding my way to this solution was a tortuous route as it is difficult to discover what is actually happening in this case. Perhaps the next edition of the book could have an extended discussion of using NavUtils, particularly in the case where you have a stream of A starts B starts C etc, each potentially either using NavUtils to return or returning a result.

I am developing my own app for teaching Android Development at Aston University but following the book which I find the best source for android development. I guess I should have come straight to the forums to find this solution, took me two days to find this solution.


#12

I still don’t quite understand what is going on with NavUtils. It does behave inconsistently even within different JellyBean versions. I have the following scenario:

From Activity A I do startActivityForResult, and start Activity B From Activity B I do startActivity to start Activity C From Activity C I return to Activity B via the UP button using the code listed above From Activity B I return Activity.RESULT_OK with Intent data to Activity A

I have a virtual machine running Android 4.2.2/API 17. For that platform the following code works fine

@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: Intent i = NavUtils.getParentActivityIntent(getActivity()); if (i != null) { i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); NavUtils.navigateUpTo(this.getActivity(), i); } return true; default: return super.onOptionsItemSelected(item); } }

I load EXACTLY the same code onto a Sony Ericsson Experia Arc S, running Android 4.1/API 16 and I get different behaviour. In the last step of my scenario above:

Activity A receives Activity.RESULT_CANCELED and the Intent data is null. On the Sony Experia Arc S/Android 4.1 I need to set a different flag for the NavUtils intent:

Why does Android 4.1 behave differently to Android 4.2.2?

A consistent solution that works on both platforms is use: