So in Activity A I pass some data to activity B through an intent. Ok, everything is fine and getStringExtra returns what I expect. Then from activity B, I pass the same data to activity C. Then, when I hit the back button in the toolbar (because of getSupportActionBar().setDisplayHomeAsUpEnabled(true)), the getStringExtra in activity B is now null.
So the flow is A (passes a string)-> B(passes the same string) -> C (back button in toolbar) -> B and now the variable passes from A to B is null. How can I fix that?
Mark this a rule : Whenever you are using the toolbar back button, you should take care of specifying the launchMode of the parent activity.
In your case, what happens after you press back button in Activity C, depends on what launchMode have you specified for your activity B.
If you have't specified any launch mode, the default launch mode is standard. In this case, the parent activity (B) is popped off the stack, and a new instance of that activity is created on top of the stack to receive the intent.
If you have specified launch mode as singleTop, the parent activity is brought to the top of the stack, and receives the intent through its onNewIntent() method. That is, the previous activity is preserved.
Refer http://developer.android.com/training/implementing-navigation/ancestral.html#NavigateUp.
In your case, you want to preserve the variable, therefore you should use singleTop launchMode.
If you are extracting the data in onCreate method of Activity B then when you hit the back button in Activity C, the onCreate method of Activity B is not called again. To get more clarity on the life cycle of an Activity I really suggest you go through this developer.android.com/reference/android/app/Activity.html
Coming to your question when you start Activity B from Activity A, store the string that you sent from A to B in a global variable rather than a local one, and perform check like
public Class B extends Activity {
private String stringData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.xml_layout);
if(getIntent().hasExtra(yourKeyName)) {
stringData = getIntent().getStringExtra(yourKeyName);
}
//Do other stuff
}
}
In your activity B, always check before getting the String -
if(getIntent().hasExtra(KEY_NAME))
String s = getIntent().getStringExtra(KEY_NAME);
Reason being that when you press the back button then Activity B is not launched with the same intent that you create in Activity A. Hence the extras are not present.
You should be string the string extra as a global variable and always use a hasExtra check.
I think it is different processes.
The first one, when you start activity B from activity A, you pass
the data from activity A to activity B. It is obvious, so the data
will be present.
The second one, then you start activity C from activity B, because of
activity B is not on the foreground, then your data will be erased.
You can persist your data on activity B by many ways.
One of them you can implement a static global variable.
Second way, you can save your variables on activity B by saving the
data onSaveInstanceState and retrieve from onRestoreInstanceState or
onCreate
Third way you can listen onActivityResult on activity B from activity
C. When you starting activity C, you must use startActivityOnResult
and passing the data that you want to pass back on activity B. After
you finish your activity C, you must return the data that originally
from activity B.
And so on...
But the better way to achieve that is the second way, use bundle onSaveInstanceState and retrieve the bundle data when onCreate.
Related
How to remove all the activities except HomeActivity.
ex:
A(HomeActivity)->B->C->D
now call B activity again removing C & D activities. I'm sending some data from D activity to B activity through intent. I have next button if i click next button then this process will be done. If I backpress then my process will be D->C->B->A
When you're going from D->B, use intent.setFlag(Intent.FLAG_ACTIVITY_CLEAR_TOP);
It will close all of the other activities on top of it and this Intent will be delivered to the (now on top) old activity as a new Intent.
FLAG_ACTIVITY_CLEAR_TOP
Then you need to create static activity variable in both C & D activity.
Just like this
//create static variable
public static CActivity cactivity;
then initialize this variable in onCreate of Activity.
//initialization
cactivity=this;
Finish that activity where you want
// use this where you want to finish activity
CActivity.cactivity.finish();
Make sure Activity must be running in background other wise you app will crash.
what I would of suggest is a not-so-smooth solution but instead of start activities for all of those replace them with a startActivityForResult, search for the result code and if it is returned from the activity (that is for you to handle of course) then call finish with all activity in the way with the result code until you reach your home activity.
Main Activity has started activity A and A has started activity B, which is currently visible on the screen: Main Activity -> A -> B.
Now Main Activity receives a broadcast intent and it needs to pass new information from it via A to B without restarting A or B, i.e., the state of A and B should remain as is. For example, if user has written someting to EditText in B, that text should not be lost.
Question: what Intent flags should be used when MainActivity sends intent containing the new information to A and A to B in order to achieve the above described behavior?
Use the activity.startActivity(Intent) method in your Activity B to call activity A ‘s “newIntent(Intent)” method, do with what you need to then :) and maybe make sure in the manifest file the activity is set to SingleInstance
Rather than figthing with intents and launch modes, I decided to save the necessary state of A and B at their onPause() method to a companion object of MainActivity and then restore the state from there at onResume() method of A and B.
I have a system which has several Activity, Activity X, Activity A, Activity B, and Activity Y. Activity X consist of list of A. If we click on A, it will go to Activity A. Activity A consist of list of B. If we click on B, it will go to Activity B. Activity B will also consist of list of B. So, if we click B on Activity B, it will go to Activity B. It is possible to end up the stack like this: X -> A -> B -> B -> B -> B -> B. According to proper navigation by Android, if we click Up on Activity B, it should go to Activity A, no matter how deep the stack is. So, every B on the previous stack should end up on A. Up until now, it's simple. I just need to set the parent class of Activity B as Activity A.
The problem is I can go to Activity B from Activity Y. If I open the Activity B from Activity Y, this Activity B is not always know how to open Activity A because the parent of B can be another B or A. B only know his parent. The problem is how to detect that Activity A is reachable automatically without creating a new instance from Activity B? This way, I can make my code like the following on Activity B.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
if (canGoUpToActivityA()) { // this means this Activity B is opened from Activity A directly (A -> B) or indirectly (A -> B -> ... -> B), not from Activity Y
NavUtils.navigateUpFromSameTask(this);
return true;
} else {
Intent intent;
if (TYPE_B.equals(mParent.getType())) {
intent = new Intent(this, ActivityB.class);
} else {
intent = new Intent(this, ActivityA.class);
}
intent.putExtra(EXTRA_ITEM, mParent)
startActivity(intent);
return true;
}
default:
return super.onOptionsItemSelected(item);
}
}
EDIT
If anybody down voting me because they think the solution is as simple as singleTop to Activity A, that's mean you guys don't understand my problem. See the last sentence of my first paragraph. I don't have any problem with going up from Activity B to Activity A, no matter how deep it is if Activity B is opened from Activity A.
Without the above onOptionsItemSelected, I can go up to Activity A from Activity B if Activity B is opened from Activity A directly (A -> B) or indirectly (A -> B -> ... -> B). This is not my problem.
The problem comes when I opened Activity B from Activity Y. I can't just go back to Activity A because Activity A rely on information from Activity X. If I open Activity B from Activity Y, then I go up, I should go to the parent of Activity B, where it can be A or another B.
The above code without canGoUpToActivityA() part will solve my problem. With that code, when I open Activity B from Activity Y, then going up should always go to the parent of B. That's already correct (B only know its parent, which can be not A).
But, when I open Activity B from Activity A, then I go up, it will launch the parent of Activity B. If I open Activity B from Activity A directly (A -> B), it's is indeed what I want. But, when I open Activity B from another Activity B (A-> B -> ... -> B), that's the problem. Because I should go up to Activity A, not the parent of Activity B which can be another Activity B.
I'm still not clear on what exactly you want, so I'll break this up into snippets:
If you want to launch a parent of Activity B and ensure that it isn't "launching itself", you can use the singleTop flag as noted by others to ensure that only one instance of B is ever on the top. You mentioned you don't have an issue with that, but you've listed that as an example, so just keep that in mind.
You've also mentioned that you check if the parent is A before launching Activity A. If all you want to do is actually launch the previous activity in the stack, there is no need for an Intent. Just finish your activity and it will exit the stack, showing the previous activity.
If you have a set activity that you should launch when going back, you can simply launch it with an intent. At this point, it doesn't matter what the previous activity is, because you are explicitly starting an activity for a given class. Again, you may look at the FLAG_ACTIVITY_REORDER_TO_FRONT flag to ensure that an existing activity is not recreated.
Lastly, if you have some complex logic that requires you to know the full stack history, you can always pass data through your bundle. A simple example is an arraylist of the simple class name so you know everything in the current task.
Edit: Since you've also added that you depend on information from previous classes, I take it that you need more than just the class name.
Note that you can always pass data through the bundles so that they are retained through all subsequent activities. You can also pass all the current data by calling putExtras.
And if your entire flow is geared towards going to a child and then passing data back to a parent, consider using [startActivityForResult](https://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent, int)). That way, you can set result codes and data and finish your activity to directly pass information to the previous activity, without launching a new intent in a forward flow manner.
So i have AppCompatActivity A, B and C. From A, i start activity B, passing an Intent I0. In B´s onCreate i get I0 and initialize the UI based on that data. Now i open C (from B). When i press the app:navigationIcon back button of the android.support.v7.widget.Toolbar from C, B's onCreate is called again, but now the Intent I0 does not contain the data from getStringExtra("data") anymore.
I have tried to use onSaveInstanceState and onRestoreInstanceState as mentioned elsewhere, but onRestoreInstanceState is not called, and the savedInstanceState Bundle is null in onCreate.
So i'm wondering if there is a way to preserve either the UI state of Activity B (not destroy it?) or the Intent I0's extra data?
I've created an Example on Github
Thanks to #pskink for suggesting to set android:launchMode="singleTop" on Activity B. This solved the problem for me in that B is not recreated with an invalid Intent.
All,
I am performing a navigation between 3 screens with dependency on each other.
Activity A is having a table with list of table rows. Each row has onclick listener event where in get the data from table row and passes to next screen Activity B.
Activity B starts with reading data from Intent and gets corresponding images & data etc and builds layout. It has a button where in reads data from edit text and passed to Activity C.
Activity C also builds layout based on data from Intent.
I acheived navigation among these activities with Parent activity configuration in Manifest file.
Now when I am doing back navigation in Activity C to Activity B, it's crashing because it could not find data from Intent as the control is coming from Child to Parent rather than Parent to Child.
I understand onCreate function of Activity B is executing again. So I kept onPause() and onResume() method in Activity B to hold the re-execution. But no luck.
Could some one advise how to handle this kind of scenario.
EDIT:
Activity B:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_activity);
getActionBar().setDisplayHomeAsUpEnabled(true);
source_activity = this.getIntent().getStringExtra("source_activity");
Bundle b = this.getIntent().getExtras();
if(b!=null)
DashBoardDisplay_l = b.getParcelable("obj");
Name_ref=DashBoardDisplay_l.getName();
}
Activity C:
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
overridePendingTransition(R.anim.activity_open_translate,R.anim.activity_close_scale);
setContentView(R.layout.point_log_activity);
Intent intent = getIntent();
String YourtransferredData = intent.getExtras().getString("ref_name");
Log.d(TAG, "ref name "+YourtransferredData);
Name=YourtransferredData;
Log.d(TAG, "name value "+Name);
getActionBar().setDisplayHomeAsUpEnabled(true);
new BackEndJob().execute();
}
Activity B contains some Intent which comes from prior screen Activity A. So while coming back to Activity B from Activity C, it could not find the data from Intent due to which it is resulting to null pointer exception.
So is there any option to display what is there in back navigation stack rather than recreating it with Savedinstance advised by Dalija.
From your description it looks like back navigation to Activity B should be prevented. So when user presses back button in Activity C it would go back to Activity A. If that is the case then you should finish Activity B when it passes the control to Activity C.
...
this.startActivity(intent); // start Activity C
finish();
}
Finishing activity will remove Activity B from navigation stack and completely destroy Activity B solving your back navigation issue.
However, if you do want to navigate back to Activity B then you should use onSaveInstanceState() and onRestoreInstanceState() in Activity B to save its state.
Recreating Activity
When you launch Activity C, Activity B is stopped. At that point system may decide to destroy Activity B at any time if it needs to clean up resources. You don't have any influence on that process and you cannot prevent it.
You can find more in Activity Lifecycle