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.
Related
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.
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.
I want to start my MainActivity with a new Intent in my other Activity. The two Activities are in the same app, and the second Activity is actually started from the MainActivity. So the scenario is like this:
MainActivity is created with an Intent
MainActivity starts SecondActivity (but MainActivity is not destroyed yet. It is just stopped)
SecondActivity starts MainActivity with a new Intent (SecondActivity is not closed)
The MainActivity is not flagged. I mean, the Activity's launch mode in the manifest is not set (so, it's default).
I want to know what happens to MainActivity's lifecycle and intent.
Is the Activity re-created? Is onCreate() called? Then is onCreate() called twice, without onDestory()? Or the new MainActivity is newly created and there will be two MainActivities? Will the Intent from getIntent() overwritten?
I know Activity.onNewIntent() is called for singleTop Activities. Then in my situation onNewIntent() is not called?
Thanks in advance.
Is the Activity re-created? Is onCreate() called? Then is onCreate()
called twice,
Yes, yes, and yes, because the default launchMode of an activity is "standard". Activity with standard launchmode will create a new instance how many times you want.
Will the Intent from getIntent() overwritten?
AFAIK, It's still the same Intent.
If you call startActivity() for an Activity with default launch mode(i.e, you didn't mention any launch mode in either in manifest or in Intent) a new instance of the activity is created.
For example, A launched B and again B launched A then Activity stack would be A - B - A. Pressing back key at this point would take you to B then A.
Your can refer to Tasks and BackStack documentation from Android.
I have an Android application that contains two Activities.
Activity A has a button that launches Activity B using Context.startActivity(Intent intent).
There is also a Notification that opens Activity in the same way.
If I start B from this notification and press back button - it just closes B and does not shows A like I go there with normal case.
Is it possible to force B to bo back to A if started from notification without history stack ?
Solution
As stefan and Paul Lammertsma mentioned, the best way is to start A from notification and in A create new intent with B - but not in onCreate() !
I dig this a bit and found that if I set in AndroidManifest a new property for A activity:
android:launchMode="singleTask"
there will be in A activity called
onNewIntent(Intent intent)
And there we should checl if Intent containst extra value passed from notification - and if so, then we call new B intent.
Thank you both and good luck with it for next devs ;-)
I would suggest having the notification call Activity A (instead of B directly) with some flag in its extras bundle. In A's onCreate(), check for the flag, and immediately launch Activity B. This will ensure that pressing back on B will return to A.
An easy way to achieve this would be to actually start Activity A from your Notification with a flag to call Activity B instantly.
So you just have to put an extra to your intent you are starting in your Notification and you have to check in Activity A if this extra exists and if it exists then you start Activity B.
Update: another way, but in my opinion not so good, would be to override the onPause() method of your Activity B and call Activity A there.
Maybe not the prettiest solution, but nevertheless a quick one;
add a boolean extra to the intent launching B, "launchedFromNotification" or something like that.
In activity Bs onCreate() you store that boolean value for later use.
In activity Bs onBackPressed() you can check the value of your boolean and if true, launch activity A before calling finish();
A prettier solution may be to launch activity A from the notification, with an extra telling it to directly launch activity B.
In our requirement, we need to refresh A activity after B activity finish.
but B activity is set as singletask.
Is there any flag or another API can fulfill this requirement?
thank you.
AFAIK, the only way to achieve the thing you want - using broadcasting: when B has been finished - send broadcast and in A catch it.
btw, is it so necessary to use singletask?
If you want to set some result to activity A from Activity 2 then you have two options
1) If you have one activity already in activity stack then you have two options
1=> Use startActivityFroResult() and set result back to calling activity
2=> Make fields in activity A as public static and assign values from activityB and onResume() of ActivityA refresh view of ActivityA
2) If you have not ActivityA in activity stack then just pass the necessary values with the intent and from ActivityA onCreate() method fetch all values from intent and load view