I read many threads about this subject, but I couldn't find anything that fits my needs.
I have an application with 3 activities (I'll call them screens here, more intuitive): A (MainActivity), B and C.
Screen A starts another process, retrieves some information from that process and sets the layout according to the answer. Then it gives you the opportunity to do something (write a text). This is done on OnCreate().
Screen B is not very interesting (presents the text and allows you to edit it).
Screen C has a single button which exits the application (basically starts the home activity). C also closes the process we started on A (after communicating some info to it).
So far so good.
The problem is that when I restart my app, I see screen C again. This is not good for me, because I want the user to be able to edit the text, without having to go back to the first or second screen (not because it's so difficult, but because I need him to see the new info presented on screen A before he does anything. And this can't be done after C, because after C he's done, unless he decides to start the app again).
This means that when I get to screen C and press the button, my app is completed and there's nothing left for me to do there (but start over fresh). But if my app was suspended on screen B, there's no need to start fresh - I can just go back to it and continue from there (regular Android behavior).
So I set android:clearTaskOnLaunch="true"but now I have two problems:
1. It now starts fresh again (sort of, see #2) even if the app was suspended on B, which is not what I need.
2. It's not really starting fresh.... Yes, I see screen A. But all the things I did on OnCreate() are not done again, which means my other process is not up and running, and then my app crashes because I have no one to talk to.....
Is there any solution to such cases?
I need to start fresh (but really fresh, with all the things I did on OnCreate() done again) only after previous run of the application was completed (we pressed the single button on screen C and got back to the home screen).
In any other case of suspension (in A, B, and even C if we didn't press the button yet) I want the normal Android behavior (i.e. go back to the same screen).
Any help will be much appreciated.
To solve this you can take advantage of the Android Activity Lifecycle: http://developer.android.com/training/basics/activity-lifecycle/stopping.html
Simply call finish() in Activity B and C in onStop() or onPause() appropriately to wipe away those activities (or "screens" as you say) when the app is closed. Activity A should reopen holding the last state when you open your app back up.
If you're looking to wipe out the last state in Activity A as well, then add a finish() call to it as well where appropriate (probably onStop or onPause again per your use case)
Good luck!
I had a similar app before, a survey app, and had the same situation. A->B->C and once you finish C you go straight to A and can't go back but you can go back from B->A. Here what I did:
1) Add this to A Activity when you start B Activity:
Intent start;
start = new Intent(MainActivity.this, RecordName.class);
startActivityForResult(start, 2);
and this method in your A activity:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 2) {
if(resultCode == RESULT_OK){
//String result=data.getStringExtra("result");
finish();
}
}
2) In B Activity add this when you start C Activity:
Intent start;
start = new Intent(B.this, C.class);
startActivityForResult(start,1);
and this again in your B activity:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1) {
if(resultCode == RESULT_OK){
//String result=data.getStringExtra("result");
finish();
}
}
3) In your C activity add this to your press method:
Intent start;
start = new Intent(C.this,A.class);
// pass any data back to A activity
start.putExtra("Key", String);
startActivity(start);
// to close previous activities
Intent returnIntent = new Intent();
//returnIntent.putExtra("result","finish");
setResult(RESULT_OK,returnIntent);
// close current activity
finish();
I hope this helps
Related
My App has two activities. Let's just call them "A" and "B" (B is a very heavyweight game). The way it's supposed to work is that "A" launches, validates the OBB package, mounts it and then launches "B" and passes some data to "B" and then "A" calls finish on itself since it's done.
It's important that "A" is the only thing that can launch/create "B".
What's happening is that when "B" gets terminated by the OS because of memory pressure, and then sometime later the user task switches back to it via the "recents", Android tries to create a new "B" activity.
In that situation, I want it to start a new "A" activity which will then create "B". Can someone tell me what I should look into to do this?
It's important that the user be able to switch between apps, take a phone call etc. and return to the already created/running "B" activity if it's still there and hasn't been terminated. If it HAS been terminated, tapping on it needs to launch activity "A" so that the OBB can be verified and remounted etc.
NOTE: I've had to simplify and gloss over some complex things to make the question short but let's assume that the preparation that "A" does cannot be moved to "B". I know that looks like a sane work around at this simplified level...but sadly it's not.
Pass some data in an Intent when opening Activity B from A, then every time Activity B is created check if that data is there, otherwise create an Intent to Activity A and terminate Activity B.
Activity A:
Intent i = new Intent(activity, ActivityB.class);
// Pass your data or otherwise a boolean like so
i.putExtra("validIntent", true);
startActivity(i);
finish();
Activity B:
Bundle loginBundle = getIntent().getExtras();
boolean continueActivity = false;
if (loginBundle =! null) {
if (loginBundle.getBoolean("validIntent");) {
continueActivity = true;
}
}
if (!continueActivity) {
startActivity(new Intent(this, ActivityA.class));
finish();
}
To preserve the data/boolean while paused, you could use saveInstanceState.
In my App, Activity1 launches Activity2 with "startActivityForResult()".
When Activity2 is done, the following code is called:
Activity2.java
private void finishActivity(final boolean accepted)
{
Intent returnIntent = new Intent();
setResult(accepted ? RESULT_OK : RESULT_CANCELED, returnIntent);
finish();
}
Activity1 receives this callback, and if the result is not RESULT_OK, it also finishes.
Activity1.java
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == Globals.REQUEST_CODE_TOS)
{
if (resultCode != RESULT_OK)
finish();
}
}
After Activity1 finishes the app closes, as expected.
THE PROBLEM --
However, after my app closes, instead of going to the home screen, a previous application on the stack briefly launches forward, then closes also! Then I get to the home screen.
In the Android bug tracker, I see a similar bug listed for the case when the back button is pressed, which I believe is the same as calling finish():
https://code.google.com/p/android/issues/detail?id=71067
I'm seeing this on a Nexus 5, running 4.4.4.
Is this just a bug? Or are there any workarounds for the behavior?
EDIT --
To clarify the behavior I desire:
In Google Maps, if you decline the TOS popup, the app closes and you go directly to the home screen. There is no awkward flash to some previously opened app.
This is decidedly the same issue as the one you linked in your question (I've been following it for months, as I own a Nexus 5 myself). It was actually fixed in the AOSP, but that particular fix doesn't seem to have found its way into the N5 yet. I've got my fingers crossed for a fix in Lollipop, but we'll see when that rolls out.
As for a workaround, I don't think any exists at the moment, although if I'm wrong on that I would love to know - I work on an app myself which does something similar (manually calls finish() the bottom-most activity in the stack when closed via the back button).
Might be the empty Intent you return via setResult(). Have you tried null here, instead?
I am puzzled over this behavior I am experiencing in an application I am developing...
Short:
Intent data is not clearing out when the user presses the back button to leave the application and then presses the recent button to re-enter the application. (Every other case, the intent data is cleared out)
Long:
I have an application with a splash screen that is used to collect data that is passed in from a URI scheme. I then setup an intent to forward the data to the main activity. The main activity has fragments and is based off the master/detail template.
The intent data is cleared out in all cases, such as pressing the home button and then going back to the application, pressing the recent apps button and then going back to the application, etc. The only case where the intent data is not cleared out is when the user presses the back button and then the recent apps button to get back into the application.
Relevant snippets of code that involve the intents:
// Splash Screen Activity
#Override
protected void onPostExecute(Void result) {
// Data is done downloading, pass notice and app ids to next activity
Intent intent = new Intent(getBaseContext(), ListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("id1", id1);
intent.putExtra("id2", id2);
intent.putExtra("id3", id3);
startActivity(intent);
finish();
}
// ListActivity retrieving intent data
Intent intent = getIntent();
if (intent != null) {
this.id1 = intent.getExtras().getString("id1");
this.id2 = intent.getExtras().getString("id2");
this.id3 = intent.getExtras().getString("id3");
}
// ListActivity clearing intent data
#Override
public void onPause() {
super.onPause();
// Clear intent data
Intent intent = getIntent();
intent.putExtra("id1", "");
intent.putExtra("id2", "");
intent.putExtra("id3", "");
}
I want to note that I have also tried using intent.removeExtra("id1") but that too did not work.
Any idea what is going on? It is as if Android is keeping the old intent even though onPause() is always called to clear the intent data.
actually this is due to Android starting the app from the history hence the intent extras are still in there
refer to this questions Android: Starting app from 'recent applications' starts it with the last set of extras used in an intent
so adding this conditional to handle this special case fixed it for me
int flags = getActivity().getIntent().getFlags();
if ((flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
// The activity was launched from history
// remove extras here
}
I believe the difference here is that the Back key is actually causing your Activity to finish, where as pressing Home causes your activity to be paused, but not finished.So when your process is brought back to the front in the Home case, it is simply resuming an already existing Activity instance, whereas in the Back case, the system is instantiating a new copy of your Activity, calling onCreate(), and handing it a fresh copy of the last Intent recorded for that activity.
In onPause() you are clearing the extras in a "copy" of the Intent. You can try adding
setIntent(intent);
to onPause() after you've cleared the extras (although calling removeExtra() would probably also work instead of setting extras to empty strings).
NOTE:
However, I would suggest that this design is flawed. You shouldn't use the Intent to keep track of state in your application. You should save some state in shared preferences because this will survive your app being killed/restarted, a reboot of the phone, or whatever.
The problem is that the new Intent is not persisted, so that If the user presses the HOME button and your app goes to the background and then Android kills your app because it is not active, when the user returns to the app, Android will create a new process for your app and it will recreate the activity using the original Intent.
Also, if you read the documentation for getIntent() it says that it returns the Intent that started the activity.
To get around the issue I was facing, I opted to use SharedPreferences as a means to pass data between activities.
I know SharedPreferences isn't typically used for this purpose, but it solved my issue and works.
I am launching a media intent to take a photo. After the user took a picture and gets back to the previous activity the whole application has restarted.
As this doesn't happen all the time, I think my application goes to the background and android kills it when the device has low memory.
Is there any way to keep my application from going to the background?
Thanks!
This is normal behavior in Android, all activities currently not visible on screen (after onStop) can be killed without notice (i.e. onDestory) if the system has low memory.
This usually happens to our app on one of our test devices which has memory issues regardless of our app.
You can usually recreate this behavior when you open the camera via the intent, and then rotate the device (portrait to landscape), this will also kill and re-create your app.
To solve this you need to extend onSaveInstanceState and use the savedInstanceState parameter in your onCreate to pass from your killed instance to your new instance some important information (like "we're in the middle of getting a pic from the camera").
I can think of two possibilities...
It's not killing the activity, but the intent launches a new activity. You can stop this by putting a tag in your manifest.xml as an attribute in the activity tag like this:
<activity
android:name=".nameOfActivity"
android:launchMode="singleTop" />
Make sure that the media intent is under the activity to handle the photo, and not a main/launcher activity.
IMO two options:
Implement onSaveInstanceState/onRestoreInstanceState
or
Make your activity a service.
I faced this issue and got a solution after R&D for it.
Set Target Android 4.0 and then add this line in AndroidManifest.xml of activity:
android:configChanges="screenLayout|uiMode|orientation|screenSize|smallestScreenSize"
It works for me.
When you start media intent use following method instead of startActivity(intent)
startActivityForResult(intent, REQUEST_CODE); //private int REQUEST_CODE = 232
When the started activity finishes, your calling activity will be started. You need to handle this using following function
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
//Perform your task
}
}
The activity started need to override follwoing method
#Override
public void onBackPressed() {
Intent intent = new Intent();
setResult(REQUEST_CODE, intent);
super.onBackPressed();
}
startActivityForResult() is an special way for starting activity for a specific task and get the desired result. The called activity will send the data back.
You can use the method putExtra() & getExtra in intent to send and receive data between two activity.
This will solve your problem hopefully.
If you have doubts comment. And if possible share your code so it can be more clear.
In my app, I start an eight step "wizard" from my landing page in which data is passed from step 1 all the way to step 8. For keeping the data intact while still in between steps, I am not calling finish() on either of the activities. However, when all the steps are complete, is there a way that I can close all the 8 activities I had started and return back to the landing page?
An illustration of sorts here:
Home - Step 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
At this point, when the user clicks "Save", close all the Steps (8) and go back to the Home Page. I have been creating a new intent to do this so far, but i realize this is not the best solution. A simple back press takes him back to the 7th Step.
Any help appreciated.
Intent intent = new Intent(this, Home.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
This will kill all the activities in between from your 8th screen and launch your hom screen back. also u can set ur home screen's acitivty in manifest launchmode="singleTop". see this link - developer.android.com/guide/topics/fundamentals.html#acttask
Another approach would be to use StartActivityForResult(...) to start each activity, and have activities call setResult() before finish(). Then in each Activity's onActivityResult(...) method call finish() if the intent is non-null.
This will create the full stack, and automatically chain-finish them all when the last finishes.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data == null) {
return; // back button, resume this activity
}
// Propagate result down the stack.
setResult(0, data);
finish();
}
This gives you a little more control and lets the original activity receive the result via onActivityResult rather than the create intent, which might be more intuitive if the original request has other state you want to preserve (in its start intent, in particular).