I have an application with several Activities.
My A Activity has the Manifest Intent filter parameters: action.MAIN
and category.LAUNCHER. after its being loaded I call Activity B and
finish() A since I don't use it anymore.
After I run my application, go from Activity A to B and press the Home
button, when I relaunch it from the applications menu or from the
Market app for ex.(not by a long press on the Home button), starts
from the A Activity and do not save its last Activity B.
I definitely know that this is possible to relaunch an application
from its last Activity (some applications from the Market do support
it) and I think that this can be determined by the Manifest parameters
but I don't know which one.
does anyone know how to implement it so my application can relaunch
from its last Activity B?
Thanks
ayanir
Though I know this is an old question, I was struggling with this very issue and couldn't find an answer on SO. So, here is my (very newbie) answer:
No, I do not think it's possible to do this by messing with the manifest - you can only launch one fixed activity per app from the home screen. What you can do, though, is launch whatever activity you want from that starting point, and Android can do it quickly enough that you never see the first one.
Though this feels very much like a hack, I implemented this routing in the starting activity's onResume() method, and used sharedPreferences to keep track of which activity to launch:
final Class<? extends Activity> activityClass;
SharedPreferences prefs = getSharedPreferences("sharedPrefs", MODE_PRIVATE);
int activityID = prefs.getInt("whichActivity", -1);
if (activityID == Constants.ACTIVITY_ID_MAINSCREEN) {
activityClass = MainScreen.class;
} else {
activityClass = null; return;
}
Intent newActivity = new Intent(this, activityClass);
this.startActivity(newActivity);
There have been a number of very similar questions lately. It's a good idea to search the site first to ensure that duplicate questions don't get asked.
For example, the question linked below says that this behaviour was happening because the developer was starting their application using the Eclipse debugger. Another person was having this problem because they were launching the application directly from Eclipse, rather than starting cleanly by manually pressing the launcher icon.
Android: keep task's activity stack after restart from HOME
so there are a few things to consider when developing Apps in Android. And one of the big things is the Application Lifecyle, if you haven't yet then I would suggest this video. What happens is that an application can be killed and reset at any point in time and you need to save the state of your application so that you can restore it at any time. If you open your App from the Launcher you will always go into the Activity that starts the app, if you want to skip to the next Activity you need to store that information and then jump to the Activity in your code.
Also have a look at this documentation about SavingPersistentState
Related
I am building an Android app, which starts a service that keeps running in the background. When the user uses the Home button to close one of the activities (which communicate with the service), and then navigates to the app again, they get the last activity. This is correct behaviour.
However, when I explicitly stop the service (via an option in the menu), the app should "Quit". Currently, that works like this:
stopService(new Intent(this, MyService.class));
finish();
// just go to to the home screen, as it is intended in Android
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
and that works fine. But when I now navigate to the app again, it starts the previously opened Activity. What I want is that it starts the starting activity again (the launcher).
How could I accomplish that?
System.exit(0);
But you should have only one Activity in stack.
If you look at the description of android:excludeFromRecents it says:
Whether or not the task initiated by this activity should be excluded from the list of recently used applications ("recent apps").
isn't this what you are looking for ? Once the user hits exit, this app won't be in the recently used apps and hence will be completely removed from memory ? :)
I think that removing 'the app completely from memory on exit' is not the issue. Even if you stop your service the process is likely to remain in memory - that is the systems' decision.
You want an explicit, custom behaviour - user chooses your 'exit' option from menu --> next app launch starts at main activity - so you will need to handle this explicitly.
The default behaviour is that the user returns to their last activity when they re-launch. So they only return to the main activity if they exit by 'backing out' through your activities. Your use of an 'exit' menu option is non-standard (not to say it is bad).
One option would be to do the backing out through your activities yourself. Perhaps now you are just finishing the current activity and you should instead finish all the activities in the back stack.
Another option is that you could save a flag, and when the UI is launched again you could read that flag and launch the appropriate activity (but again be careful with the backstack). The easiest way to set that flag is probably by overriding onSaveInstanceState(Bundle) and then reading the bundle in onCreate (or use sharedpreferences).
Have you tried setting the clearTaskOnLaunch to true for your main activity (in your manifest).
From the documentation:
When the value is "true", every time users start the task again, they are brought to its root activity regardless of what they were last doing in the task and regardless of whether they used the Back or Home button to leave it.
This seems to describe what you want to do. I have never tried this personally, but it seems worth investigating for your purpose.
I'm working on a widget for an Android app and I encounter a problem. When the user click on the widget, I'd like the application to open his first activity (the one with intent-filter: 'action.MAIN' / 'category.LAUNCHER') (I'll call it Activity A).
To do that, I use this snippet (in my WidgetProvider class):
Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.view , pendingIntent);
This works very good if my application is killed or if all the activities have been closed (using the back button until the application close)
The problem happen when several activities are open. Imagine the activity stack is like : Activity A -> Activity B -> Activity C.
If I click on the widget, SOMETIMES the application is just brought to front and Activity C is visible instead of Activity A.
It's very strange because this doesn't happen every time. (It seems that it happen after I navigate a lot in activities)
I really need the activity A to open and not another activity whatever how the activity stack was because this activity is like a hub with several links to differents fonctionnalities.
Can somebody help me?
Thank you very much.
PS: Sorry if my English is pretty poor, I'm from Belgium
From a widget you should do more or less the same as the system launcher does with intents.
Try using FLAG_ACTIVITY_CLEAR_TOP in your intent. You will also want to look at FLAG_ACTIVITY_NEW_TASK.
It's better to follow the platform guidelines by the way, which say that you can also inject deep into your app, provided you give the user the right path to navigate up again.
If you want a specific Activity to launch, why don't you just call that activity specifically, instead of dynamically.
You are experiencing issues due to the Activities in your back-stack. Android manages the life-cycle of your Activities in a very special way that you should probably research - http://developer.android.com/reference/android/app/Activity.html#ProcessLifecycle
calling finish() - will always return to the activity that launched the current activity. The only time your app will close, is if there was not an original activity. You could always test for this condition, and if the calling activity is NULL, you could explicitly call you Activity A.
The problem is somewhat odd and after having trying to figure it out for about a day now, I am posting it here.
I have an application where an activity A(main activity) launches other activities(B,C or D).
The issue here happens when activity A has started Activity B and 'home' button is pressed.
Case 1 - When I test my application in debug mode on my device (HTC Desire) after pressing the 'home' button, I again click the application icon, it returns to the same activity (activity B), which is what is should do. No issues here.
Case 2 - When I export the signed package, and then install the application on the same device, then if I click the application icon after pressing the 'home' button, then a new instance of activity A (main activity) is launched ON TOP of activity B. I got to know this because when I press 'back' from that activity, it returns to activity B and pressing 'back' again shown activity A.
The behavior ceases to exist if the application is quit in the same order it was started, that is, if I press 'back' from activity B, then 'back' from activity A (exit).
After this everything runs fine.
I have tested it many times with different settings but I can't seem to figure out why the behavior is like this.
Any help is appreciated.
I think giving Activity A the 'single top' flag in your manifest should fix this.
Regarding Case 1:
When launching your intent from Activity A to start Activity B, add the flag FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
This will ensure that when you go home and launch the the app again, Activity A will be shown.
Regarding Case 2:
I'm not exactly sure how this would occur. It seems like it thinks you have two versions of the app, the signed one and the unsigned one but keeps them both in the same task stack. You may want to consider using singleTask or singleInstance for your Activity if you only want one instance. See the doc on tasks and back stack for more details.
I would agree with Noel regarding the likely cause of Case 2. Without task reparenting or it being set to a launchmode preventing multiple instances of an activity, there is a chance that launching it from Home isn't deemed the same stack as launching it from Eclipse (assuming this to be the case).
In my talent calculator app I have the whole application set allowTaskReparenting=true to ensure nothing is left in other stacks (primarily email as it can email launch urls). I then have my main activity set to launchMode="singleTask" as I only ever want one instance of this to exist no matter what launches it or with whatever intent.
My only other activity is for loading and saving and that has noHistory="true" to make sure it is removed and never returned to. That basically means it only exists while you're in it, and can never return to it.
clearTaskOnLaunch="true" will also ensure only the main Activity remains in the stack when it's launched from Home, but this isn't always the case if you have other ways to get into your activity. If it's only ever launched from Home then set this.
Hope that all helps.
Do you start you application manually or using Eclipse or another IDE? When starting from Intellij IDEA I had exactly the same problems. Then I stopped and ran it manually and behaviour was as expected.
For testing purposes only, I am allowing my app APK to be downloaded and installed via a URL. Once downloaded on the phone, it can be launched with the Android app installer which gives the user an option to install it to their device and then run it.
Consider if we downloaded and ran the app in the way described above. The main/launcher activity in my app is a login page (Activity A). Once the user is authenticated, they are taken to the main area of the application, e.g. Activity B. So now the current activity stack of this task is A > B.
I then press the home button on the phone and am taken to the Android home screen. I re-launch my app via the icon in the menu, and I am taken to Activity A, instead of Activity B. Either the activity stack is now A > B > A, or there are now two separate tasks with activity stacks A > B, and A respectively. What I want is to be taken back to Activity B when I relaunch the app. Pressing back whilst in this state will take me back to Activity B.
This undesired behaviour only happens if I first open the app via the installer, and not if I open the app via the home screen/menu.
I looked into how the activities are being started by each mechanism. When we use the app installer, we see the following logs:
INFO/ActivityManager(XXXX): Starting activity: Intent { dat=file:///mnt/sdcard/download/[my app].apk cmp=com.android.packageinstaller/.InstallAppProgress (has extras) }
INFO/ActivityManager(XXXX): Starting activity: Intent { act=android.intent.action.MAIN flg=0x10000000 cmp=[my package]/[Activity A] }
via launcher / home screen:
INFO/ActivityManager(XXXX): Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=[my package]/[Activity A] }
When started with the installer we see it is using the flag 0x10000000, but when started with the launcher we see it is using 0x10200000. It is also using an intent category.
From the docs we see the flags are:
public static final int FLAG_ACTIVITY_NEW_TASK
Constant Value: 268435456 (0x10000000)
public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
Constant Value: 2097152 (0x00200000)
The flag FLAG_ACTIVITY_RESET_TASK_IF_NEEDED (which is being used when the app is launched from the launcher) seems to usually prevent a new task from being created if one already exists, and will restore the last used activity. This is the desired behaviour. Why is it not working in this situation? Is there anything I can do to make sure my application will always return me to the last Activity regardless of whether it was started through the app installer/launcher?
If I use singleTask it will always take me back to the main activity (Activity A) whenever I run the app (which is also not desirable).
Here is a question I found where someone is experiencing a similar problem (which has no accepted answer): App loses its ability to remember its stack when launched from another application
EDIT: Checking for the flag FLAG_ACTIVITY_BROUGHT_TO_FRONT in onCreate() of our launcher activity (and then finishing if it is set) seems to fix the main symptom, but clearly the underlying issue is still there. Is there a more complete fix?
EDIT2: The same result occurs when you download/run the app from the Android Market, so some of the above details may not be relevant.
Added the answer that antonyt provided:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
// Activity was brought to front and not created,
// Thus finishing this will get us to the last viewed activity
finish();
return;
}
// Regular activity creation code...
}
The underlying issue I believe is that the Intents used are different between the launcher and the installer. In so far as you are getting different Intent flags you are going to get different launch behavior. You can muck with the launch modes and you may be able to get a consistent result but fundamentally those different Intents will produce different results.
Your fix (or something like this) is probably your best bet.
Your problem is likely rooted in the fact that App installer doesn't use the LAUNCHER category, as does the launcher.
This bug has been documented elsewhere:
App always starts fresh from root activity instead of resuming background state (Known Bug)
I am developing an app with numerous Activities. I would like to create a persistent notification that (more or less) says, "AppName - Return to AppName" that will be present whenever my background services are running. Creating and disposing of the notification was no problem.
Now, the user could be on any of several screens/Activities, leave the application, then want to re-enter the app via the notification. The problem is, the notification must have an intent, which launches a predetermined Activity. I want the notification to re-enter the app in whatever Activity is at the top of the history stack.
My first attempt at an ugly workaround was to make an activity (let's call it "returnFromNotify") whose only job was to "finish" itself in it's "onCreate". The notification would open "returnFromNotify" in the scope of the applications history, which would then immediately remove itself, sending the user back to the previous history state in the application stack. This seems to work... unless the user has used "back" to completely back out of the app. Then, when they hit the notification, "returnFromNotify" loads, then finishes, sending them back out to the home screen (as there are no activities in the history stack for the app).
I considered trying to detect if there was anything in the history stack before "returnFromNotify", and if not, fire up my main Activity. I can't seem to find a way to do this, either.
Any input or suggestions for a Java/Android novice? FYI, My primary history is with script-based languages.
I like your original idea of creating a "returnFromNotify" activity better than your proposed workaround, as it is possible to detect if the ResumeActivity is at the bottom of the stack (and therefore the only activity in the stack).
Here's how you can do it:
Add your ResumeActivity to the manifest and specify the noHistory attribute:
<activity android:name=".ResumeActivity" android:noHistory="true" />
Specifying noHistory will make sure this Activity won't stay in the stack as soon as it finishes. This way you know that only a currently running instance of the ResumeActivity will show up in the stack.
In order to check the application stack, you'll also have to ask for the GET_TASKS permission:
<uses-permission android:name="android.permission.GET_TASKS" />
Now you can use ActivityManager::getRunningTasks() to determine if ResumeActivity is the only activity in the stack:
public class ResumeActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(isOnlyActivityInStack()) { //check the application stack
//This activity is the only activity in the application stack, so we need to launch the main activity
Intent main = new Intent(this, MainActivity.class);
main.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(main);
} else {
//Return the user to the last activity they had open
this.finish();
}
}
/**
* Checks the currently running tasks. If this activity is the base activity, we know it's the only activity in the stack
*
* #return boolean This activity is the only activity in the stack?
**/
private boolean isOnlyActivityInStack() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
boolean onlyActivityInStack = false;
for(RunningTaskInfo tasks : manager.getRunningTasks(Integer.MAX_VALUE)) {
if(tasks.baseActivity.getPackageName().equals(this.getPackageName())) { //find this package's application stack
if(tasks.baseActivity.getClassName().equals(this.getClass().getName())) {
//If the ResumeActivity is the base activity, we know that it is the only activity in the stack
onlyActivityInStack = true;
break;
}
}
}
return onlyActivityInStack;
}
}
I know you asked this question over 2 years ago, but I'm providing this answer in case anyone else runs in to this particular situation (as I did). I think you were on the right track with the solution you were originally working towards.
Okay, I believe that I have found a satisfactory work-around for my specific case. I've added a static integer to my "mainActivity", and each time it's "onCreate" is fired, it increments the integer. Each time it's "onDestroy" is fired, it decrements.
In my "returnFromNotify", I look at the static integer to see if it is greater than 0. If so, I assume there is an active "mainActivity", and that running "finish" inside "returnFromNotify" will return there. Otherwise, it assumes the users has "backed" out, finishes itself, then uses "startActivity" to fire up a new instance of "mainActivity".
This is not a universal solution, but for my purposes, I think it will suffice. I am still open to other answers, and if someone can punch a hole in my logic, please do so - constructive criticism is welcome. Thanks.
I guess there is no easy way to do this but instead of adding a counter in the mainActivity I would extend Application:
Base class for those who need to
maintain global application state. You
can provide your own implementation by
specifying its name in your
AndroidManifest.xml's
tag, which will cause that class to be
instantiated for you when the process
for your application/package is
created.
I would mantein the logic there and have a method like:
public Intent getIntentForLastActivityShown();
to be called when the notification item is clicked.
My first approach would be to use SharedPreferences and store a key value pair called something like lastDisplayedActivity. Then in each Activity's onResume (and possibly `onCreate') you would have a line like this:
sharedPreferences.edit().putInteger("lastDisplayedActivity", ReturnFromNotify.THIS_ACTIVITY_NAME);
In other words, you store an application-wide variable indicating which activity was last displayed. Then you just grab this variable from SharedPreferences and launch the corresponding activity.
I usually use activity named "Launcher" that checks state of my application model and starts activities (or does other things) depending on model rules. I put Model object in my Application class. Model can use Preferences to store its state. I do it to avoid static fields in activities.