Android: Data passed with intent disappears when coming back from deeper hierarchy - android

I have an OverviewActivity that contains a listview. When an item is selected, an intent is created to move to the DetailActivity and I pass an int with it.
This int is assigned to a private variable and is used to query the database.
DetailActivity code:
private int mIssueId;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_issue_detail);
mIssueId = getIntent().getIntExtra(IssueOverviewFragment.INTENT_ISSUE_ID, -1);
...
}
In the DetailActivity I can go to a GraphActivity. But when I press the upButton in the GraphActivity, the application crashes because the variable became -1 in the DetailActivity (and the database can thus not be queried properly).
The hierarchy is:
OverviewActivity -> DetailActivity -> GraphActivity
GraphActivity code:
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_graph );
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
...
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main_detail, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_logout: {
Utility.redirectToLogin(this);
break;
}
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
How do I retain the values of my mIssueId attribute in the DetailActivity?

The Intent intends to pass information between activities. When Activity1 pass control to Activity2 mostly related to the behavior of the activity.
If you have information you need to share across a complex hierarchy or to be available for your entire app, you can choice between Shared Preferences if you need persistence or use a Singleton class if the data only will be needed while the app is running.
This is a sample for a Singleton class keeping information to available to the entire app:
public class AppData {
private static AppData ourInstance = new AppData ();
public int score;
public static AppData getInstance () {
return ourInstance;
}
}
And how to access it:
AppData.getInstance().score = 100;
Hope it helps.

It looks like you are getting as a default value because there was an issue getting the intent in DetailActivity. You could try breaking up your request a little with
mIssueId = getIntent().getExtras().getInt();
But I think the issue is probably with how you are putting the int into the intent.
It should look something like
Intent intent = new Intent(context, DetailActivity.class);
intent.putExtra(IssueOverviewFragment.INTENT_ISSUE_ID, mIssueId);
startActivity(intent);

Related

onCreate is not called after switching back from preferences

My app only has two activities: main and preferences
In the PreferenceActivity (or in my case SettingsActivity), the user can change some stuff, that affects the MainActivity.
This is my SettingsActivity:
public class SettingsActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
public static class SettingsFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener {
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
#Override
public boolean onPreferenceChange(Preference preference, Object value) {
return false;
}
}
}
Now I have two issue:
When the user presses the HomeButton the main screen does not seem to call the onCreate so the changed preferences are not loaded in the main activity. What do I need to change, to tell android to call the onCreate function?
Since android nougat there are these "activity-change-animations", so when switching to a new activity, the new screen kind of slides in. But when the user presses the HomeButton the expected animation would be slide out, but it is also slide in. How can I fix that?
Explanation for the first issue:
There's a difference between onCreate and onResume
onCreate: Activity launched for the first time. Here is where you may initialize your stuff.
onResume: User returns to the activity after another activity goes to the background. (onPause is called on the other activity)
For the second issue, try this:
Intent intent = NavUtils.getParentActivityIntent(this);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
NavUtils.navigateUpTo(this, intent);
Instead of
NavUtils.navigateUpFromSameTask(this);

How to show a new screen from menu item

I'm new to android and trying to find out how to show a new screen when the user clicks something in the menu item.
I'm using ActionbarSherlock and looking at the sample github-android app.
When the user clicks on an item in the menu, I want to show them a new screen. Github code is doing that like so:
startActivityForResult(new Intent(getActivity(), CreateGistActivity.class), GIST_CREATE);
But I've seen some code samples do:
Intent i = new Intent(getApplicationContext(), SomeActivity.class);
My code looks like this:
public class MainActivity extends SherlockActivity {
....
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.create) {
//show createactivity class
return true;
}
return true;
}
What is the right way to do ?
You can do it just like that:
public class MainActivity extends SherlockActivity {
....
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.create) {
//show createactivity class
Intent i = new Intent(MainActivity.this, SomeActivity.class);
startActivity(i);
return true;
}
return true;
}
startActivityForResult is used when you have to return some value/data to the first screen like a user selection. More here
As far as the context to use getActivity() or getApplicationContext(), I prefer to use the context of current activity MainActivity.this its more straitforward similar to documentation example
/** Called when the user clicks the Send button */
public void sendMessage(View view) {
Intent intent = new Intent(this, DisplayMessageActivity.class);
EditText editText = (EditText) findViewById(R.id.edit_message);
String message = editText.getText().toString();
intent.putExtra(EXTRA_MESSAGE, message);
startActivity(intent);
}
Inside a fragment use getSherlockActivity() instead of getActivity() as getActivity() can cause crashes to older devices.
Of course getApplicationContext() would always work and not crash but I feel that it may mess the garbage collector and do not let activities to be cleared (but not sure about it)
Just use startActivityForResult
There is no 'right' way. The Github code doesn't first declare the variable. The onther does. I believe for a menu, you normally need to declare the Intent as a local variable, if not a field.
Create an intent: Intent i = new Intent(MainActivity.this, CreateGistActivity.class);
where MainActivity is the activity you're in, and CreateGistActivity is the class you want to launch.
Then use startActivity(Intent) to launch the new activity: startActivity(i);
Or just combine them:
startActivity(new Intent(MainActivity.this, CreateGistActivity.class));
Full code:
public class MainActivity extends SherlockActivity {
....
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.create)
{
Intent i = new Intent(MainActivity.this, CreateGistActivity.class);
startActivity(i);
return true;
}
return true;
}
startActivityForResult probably isn't needed in your case, unless you're expecting to send values between the classes.

Android Intent flags

My problem is in understanding how to correctly use intents. After googling and reading all the documentations and articles on this topic, I still cannot sort out my problem. I have two activities: "Searchable" and "ActivityWordInfo". The "Searchable" activity searches a word in the database, and displays search results or suggestions. After the user cliks one of the search results, "ActivityWordInfo" activity is launched and diplays the word definition. Here is some part of the code:
Searchable:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Get the intent, verify the action and get the query
if( savedInstanceState != null ){
//the application is being reloaded
query = savedInstanceState.getString("searchedWord");
doMySearch(query); //does the search in the database
}else{
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
query = intent.getStringExtra(SearchManager.QUERY);
doMySearch(query);
}
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("searchedWord", query);
//saves the searched word if this activity is killed
}
#Override
public void onClick(View v) { //when one of the search results is clicked
int wordID = (Integer) v.getTag();
Intent intent = new Intent(Searchable.this, ActivityWordInfo.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
Bundle b = new Bundle();
b.putInt("key", wordID);
b.putInt("calling_activity", callingActivityId);
intent.putExtras(b);
startActivity(intent);
}
ActivityWordInfo:
public void onCreate(Bundle savedInstanceState) {
...
Bundle b = getIntent().getExtras();
current_word_id = b.getInt("key", 0);
callingActivityId = b.getInt("calling_activity", 0);
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
switch(callingActivityId){
case 3: //which is the ID of Searchable activity
Intent intent3 = new Intent(ActivityWordInfo.this, Searchable.class);
intent3.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent3);
break;
}
break;
}
When the user is in ActivityWordInfo and navigates up, I expect to go to Searchable activity, which should have saved its instance state (the list of results should be still there). What in reality happens:
-The word typed by the user is assigned to 'query' variable and then the results and suggestions are displayed in "Searchable"
-the user clicks one of the words and "ActivityWordInfo" is created
-then, when the user navigates up, the onSaveInstanceState is called for the "Searchable" activity, then it is destryed and created. The result is an empty layout :(
I cannot understand why "Searchable" is destroyed and then created! This only happens in Android 4.2 and not in lower APIs (in 2.3.3 worked perfectly as I expected). Is there any difference in the activity lifecycle in JellyBean?
Note: I cannot use the parentActivity attribute in the manifest since the ActivityWordInfo is called by multiple parents.
The behavior of "up" navigation changed between 2.3 and 4.0. I suggest you look at the topic "Tasks and Back Stack" at developer.android.com.

Refreshing the activities that are present in the back stack

I have a Enter you details option on the action bar as an action item and I am setting that option visible on all activities by extending the below class.
public abstract class ActionActivity extends SherlockActivity {
protected boolean mIsEnterDetailsScreen = true;
protected userName = "";
SessionManager session;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if(mIsEnterDetailsScreen)
{
menu.add("Enter Your Details")
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
else
{
menu.add(userName)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
//This uses the imported MenuItem from ActionBarSherlock
switch(item.getItemId())
{
case 0:
Intent intent = new Intent(ActionActivity.this,EnterDetails.class);
startActivity(intent);
return true;
}
return false;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
session = new SessionManager(getApplicationContext());
sessionCheck(); // This method's body is separated for the easiness and pasted below.
super.onCreate(savedInstanceState);
getSupportActionBar().setHomeButtonEnabled(true);
}
}
So, by the above code, if the user enters details, I am saving it in preferences and at the same time setting the mIsEnterDetailsScreen = false; so that this option would be invisible and user name taken from his entered details would be visible. This works fine only for the activities that are started later. But not on the previous activities. So, my doubt is how to refresh the activities that are there in the back stack. So that even when he presses the back button, still the username has to be displayed even in the previous activities after he entered details instead of Enter you details option.
Suppose if user clicks the Enter you details action item on the 3 rd activity, the changes reflect in the later activities(4, 5, 6....), but not on 1st activity and 2nd activity. Is there any way to refresh activities so that the changes persist.
This is the sessionCheck() method in ActionActivity
public void sessionCheck()
{
if(session.isLoggedIn())
{
mIsEnterDetailsScreen = false;
HashMap<String, String> userHashMap = session.getUserDetails();
// name
String name = userHashMap.get(SessionManager.KEY_USER_NAME);
userName = name;
}
else{
mIsEnterDetailsScreen = true;
}
}
When the user enters details, save them somewhere persistent, I would suggest using SharedPreferences. Then override onResume in your Activities that will be in the backstack and load the details (from the SharedPreferences for example) there.
Android does not use an onRefresh method.
You should have to implement the override method onRefresh on the first activity.
While you close come back from Details Activity or you close/finish the Second Activity(that is your Enter Your Details Activity), onRefresh method of the First activity is called. At that time you can set the Value from the SharedPreference. So, that way you can get the Values reflacted on your view.
Hope you got my point.
Feel free for any comments.

Activity for a screen calls itself for similar usages - but going back quits app

I have one activity representing a screen that is used in two variations. The only difference is that in one case it's used to handle numbers in the other for colors. This is how it is declared:
> public class MainScreen extends Activity {
/** Called when the activity is first created. */
private Integer activityCode;
private static final int ACTIVITY_NUMBER = 0;
private static final int ACTIVITY_COLOR = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityCode = savedInstanceState != null ? savedInstanceState
.getInt("Task") : null;
if (activityCode == null) {
Bundle extras = getIntent().getExtras();
activityCode = extras != null ? extras.getInt("Task") : null;
}
do stuff depending on which activity is actually chosen
}
And this is how it is called
FROM WITHIN ITSELF
:
> #Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
Intent i;
switch (item.getItemId()) {
case OTHER_PAGE_ID:
i = new Intent(this, MainScreen.class);
if (activityCode == ACTIVITY_NUMBER) {
i.putExtra("Task", ACTIVITY_COLOR);
startActivityForResult(i, ACTIVITY_COLOR);
finish();
} else {
i.putExtra("Task", ACTIVITY_NUMBER);
startActivityForResult(i, ACTIVITY_NUMBER);
finish();
}
return true;
....
}
Is this way of re-using the same class actually OK? I use the same class for very similar screens and want to switch back and forth depending on the user selection.
BUT the class calls itself everytime a different screen is selected between NUMBERS <-> COLOR.
The problem is, that when I go from NUMBERS to COLORS and then press back-arrow the app quits. However, when I go from NUMBERS to another screen and press back, it goes back to NUMBERS again.
Why doesn't going back to where I come from work in the case where the class calls itself? I would assume it just puts each call on the stack and comes back to it.
Isn't this just a mini recursion where NUMBER calls itself as COLOR and when finished appears again?
I hope I could make myself clear. Thanks for your help
That makes my head spin. Why not just use two Activities? If it's for code reuse, just have one base class with all your common code, then extend it for your Colour and Number classes:
public class Base extends Activity {
// common code here
}
public class Colour extends Base {
// colour specific code here
}
public class Number extends Base {
// number specific code here
}

Categories

Resources