I am trying to control my stop/restart behavior according to the Android Guidelines but it's getting a bit tricky. I want my app to restore state after being minimized and tapping the launcher icon.
The current behaviour makes so I can HOME out of the app, and when the user comes back using the long press list everything behaves as expected. Now, trying to return tapping the app icon starts the default activity, it doesn't take onRestoreInstanceState into account, and while keeping the old task affinities open on their back stack.
The activities have onCreate, onResume, onSavedInstanceState overridden, calling their super, and are of type SingleInstance.
During one of my app development, I had the similar issue where app have single activity and need to save some state values (usually string, Boolean) onpause. I will give you idea how i handle it, and I hope it will useful to you also.
protected void onPause() {
super.onPause();
saveState();
}
protected void onResume() {
super.onResume();
retrieveState();
}
donot forget focus change ...
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(hasFocus) {
retrieveState();
}
else { saveState(); }
}
Now the save and retrieve data
public void saveState()
{
SharedPreferences settings = getSharedPreferences(WEBSTATE, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString(CURRENT_URL_STATE, "some url");
editor.putInt(FILECOUNT, 10);
/* any other content... */
editor.commit();
}
public void retrieveState()
{
SharedPreferences settings = getSharedPreferences(WEBSTATE, 0);
String current_url = settings.getString(CURRENT_URL_STATE, "");
int count = settings.getInt((FILECOUNT, 0);
/*use these value as per requier.. */
}
The current behaviour makes so I can HOME out of the app, and when the
user comes back using the long press list everything behaves as
expected. Now, trying to return tapping the app icon starts the
default activity, it doesn't take onRestoreInstanceState into account,
and while keeping the old task affinities open on their back stack.
Let's say you got 2 activities: A and B. Activity A is your entry point, so when you tap on app icon (which in fact is incorrect statement as this is activity icon, NOT the app icon) you launch activity A. Now you got to activity B and press HOME. When you now open list of recents, you will see activity B, and will be able to get back to it. But when you tap your "app icon" on launcher screen, you will enter activity A. And this is normal and correct behavior. Also note savedInstanceState is not overall state of your application as you most likely think.
Also note, that your Activity class can be instantiated multiple times, which depending on your application architecture can, also lead to incorrect navigation behaviour. You can try to control this by using android:launchMode in your Manifest file (you will find android documentation here).
Finally, if possible, consider reworking your application to use Fragments instead of multiple activities - that would simplify controling application flow.
Not sure if this is what you want to do, But i had a similar problem where i wanted a single base activity to return to, And i couldn't get the desired result from SingleInstance, I defined a new Activity which was the main launched :
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
And from that activity i launched my desired Activity with the desired FLAG clear-top, and finished the main launched activity.
Related
Basicly, i have 3 activities in my app. call them A,B and Login, A and B shows the user very sensitive data. a legit flow has the form of launch->Login->A->B->A->B...->A->quit what i really want is that if for some reason the app got to the background ( by pressing back or home or whatever ) while it was still on A, or on B, then no metter what way the app relaunches ( or traced in some way including by the long home press menu ) it would not show A or B content. he can see either the login page content, or nothing at all.
noHistory is almost what i was looking for, but isnt a good solution since i want to allow intuative navigation from A to B and back to A. so how is it done?
Check out the "clearTaskOnLaunch" Activity attribute.
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.
try the following pseudo:
public A extends Activity
{
boolean just_launched = true;
void onPause()
{
just_launched = false;
}
void onResume()
{
if (!just_launched)
finish();
}
}
This is a very basic question, I have a few screens, now when you go from one to another you can then press back and cycle back through all the windows.
I'd rather that when you pressed back it took you to a specific window for instance:
Menu Screen
---->User clicks Info
Info Screen
---->User clicks Ride Info
Ride Info
---->User clicks back
Info Screen
Now is this to do with the hierarchical parent, will this define where it goes back to?
The second part of my question is if I don't have any resources to release or information to store for an on-resume what should I do when the user pauses my app? At the moment if you go back to the menu screen and re-select the app it will start a new instance rather than resuming. Do I just simply implement:
#Override
public void onPause() {
super.onPause(); // Always call the superclass method first
}
and
#Override
public void onResume() {
super.onResume(); // Always call the superclass method first
}
Apologies if this is a bit basic!
You might want to look in to setting FLAGS for your intent while opening new activity Android Dev
Something like this -
Intent a = new Intent(this,A.class);
a.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(a);
There is no basic questions here :)
Easiest way to do this is to override the onBackPress() function.
Sample :
#Override
public void onBackPressed() {
//Do what you want here
}
For saving variables when users leave the app, you need to override onSaveInstanceState(Bundle bundle)
#Override
protected void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(outState);
bundle.putInt("sample", 1);
}
For your two parts:
1) It's almost always best to let Android handle the back button, the order of which is determined by the back stack: see here for an explanation. If you want hierarchical navigation, I would recommend looking into the up button - see this developer page for a good explanation of how to use the different navigation tools.
Additionally, if you don't want to have an activity appear in your back stack, you can set the attribute android:noHistory="true" in your manifest, which will mean that the user can't return to it using the back button.
2) If the user has left your app, it's automatically paused, you don't need to implement onPause or onResume for this to happen. However, it's also up for collection to be terminated by the OS. If this happens, then it will be restarted when the user opens it from the launcher again. Any previously running instances of the app should automatically be opened.
I have an Android app made of 3 activities A, B and C:
A is the application itself (a regular Activity)
B is the settings screen (made with PreferenceActivity)
C is the about screen (a regular Activity)
The relationship between the activities is as follows:
A is the main activity
B's parent is A
C's parent is B
Activity A raises a notification when going to background, this is accomplished by calling NotificationManager's notify() inside A's onStop() method.
When the user launches the app it starts from activity A, then if the user presses the home button the notification will be raised as soon as the home screen is shown. Clicking on the notification will get the user back to the app.
Everything seems to work but now there's a behaviour I tend not to like:
When the user launches the app and then goes to settings (therefore showing activity A and then B) the notification is also raised (because A has gone to background in order to show B).
This is not desired: The notification should be raised only when the application goes to background regardless of the activity the user was looking at.
How to implement this proper behaviour?
Thanks in advance,
Marco
just create a boolean flag shouldNotify and set it to false when you are opening one of your other Activities. Without your code I can't tell you exactly how to implement it but here is an example to get the idea accross:
private boolean shouldNotify = true;
#Override
public void onStart(){
super.onStart();
shouldNotify = true;
}
#Override
public void onStop(){
super.onStop();
if(shouldNotify){
sendNotification();
}
}
// Where you need to put these next snippets depends on how you've made your Activity
/*
* Launching settings activity
*/
shouldNotify = false;
Intent settingsIntent = new Intent(this, YourSettingsActivity.class);
startActivity(settingsIntent);
You could set a boolean flag which is checked before raising the notification and set it to "false" before startig Activity B.
Of course this way you would have to do the same in Activity B and C for them to raise the notification, too.
Or, as hinted to before you could implement an own application class and fetch the callback there.
My application starts with a welcome screen Activity, but that screen has an option to skip that screen altogether in future launches.
What's the proper Android way to do this? Initially, I just automatically detected the skipWelcome preference and switched to the 2nd activity from Welcome. But this had the effect of allowing the user to hit the back button to the welcome screen we promised never to show again.
Right now, in the Welcome activity, I read the preference and call finish() on the current activity:
SharedPreferences preferences = getPreferences(MODE_PRIVATE);
boolean skipWelcome = preferences.getBoolean("skipWelcome", false);
if (skipWelcome) {
this.finish();
}
And then I implement onDestroy to move on to the next Activity:
#Override
public void onDestroy() {
super.onDestroy();
startActivity(new Intent(Welcome.this, StartFoo.class));
}
But this makes for some weird visual transitions. I'm starting to think that I need a base Activity that pops open Welcome only if proper, and then goes to StartFoo.
I can't comment on Mayra's answer or I would (not enough rep), but that's the correct approach.
Hidden in the Android documentation is this important phrase for Activity.startActivityForResult(),
"As a special case, if you call
startActivityForResult() with a
requestCode >= 0 during the initial
onCreate(Bundle
savedInstanceState)/onResume() of your
activity, then your window will not be
displayed until a result is returned
back from the started activity. This
is to avoid visible flickering when
redirecting to another activity."
Another important note is that this call does not block and execution continues, so you need to stop execution of the onCreate by returning
if (skipWelcome) {
// Create intent
// Launch intent with startActivityForResult()
return;
}
The final piece is to call finish immediately in the welcome activity's onActivityResult as Mayra says.
There are a few solutions to this.
Did you try just launching the activity and finishing? I vauguely remember that working, but I could be wrong.
More correctly, in if(skipWelcome) you can start the new activity for result, then when onActivityResult is called, immidiately finish the welcome activity.
Or, you can have your launcher activity not have a view (don't set content), and launch either the welcome activity or StartFoo.
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.