What is the default implementation of onBackPressed() in Activity - android

I want to know the default implementation of onBackPressed() in Activity. How to deal with the Activity recover in the default implementation of onBackPressed()?.
The following is the issues I suffer from. I have a test Activity code like this:
public class MainActivity extends Activity {
public static boolean test = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
Toast.makeText(this,"is "+test,Toast.LENGTH_LONG).show();
test = !test;
}
}
When I first enter the app, I get 'is false'. Then I click back button and get to the home screen. After that, when I enter the app, I get the Toast 'is true'. I think the onBackPressed() should kill my app when it gets back to the home screen, but It does not. This is my question.
If I override onBackPressed() like this
#Override
public void onBackPressed() {
// super.onBackPressed();
finish();
try {
android.os.Process.killProcess(android.os.Process.myPid());
} catch (Exception e) {
e.printStackTrace();
}
}
I always get the Toast 'is false' after I enter the app.
Can anyone explain this problem and tell me what the default implementation of onBackPressed()?
I'd like to know the flow process in onBackPressed() in detail. I have read some of the source code on onBackPressed(), but I couldn't understand it well.
Thanks in advance.

The default implementation of Activity's onBackPressed() probably won't tell you a lot about the actual Activity/application lifetime. You should dig much dipper to understand the internal Android (and Linux) "mechanics" on application/process killing.
What an application developer should know is that once an Activity is in background (Home button pressed, incoming call received etc., i.e. onPause() followed by onStop() have been invoked) its process may (similar to what you did with android.os.Process.killProcess(...)) or may NOT be killed. See Multitasking the Android Way by Dianne Hackborn for the reference.
As to finishing an Activity by pressing the back button, it does not mean its instance will be immediately killed and the memory garbage collected (see this answer). It just means a new instance of the Activity will be created next time you navigate back to it.
Regarding your code and the statement that
When I first enter the app, I get 'is false'. Then I click back button and get to the home screen. After that, when I enter the app, I get the Toast 'is true'. I think the onBackPressed() should kill my app when it gets back to the home screen, but It does not.
This is the case when the system didn't kill the process while the Activity were in background (again, it is not guaranteed). If it did, the Toast would have shown false.
In order to check that a new instance of MainActivity is created each time you press the back button and then navigate back to the app, I don't recommend to use a static variable, - it appears to be not that obvious (see, for instance, is it possible for Android VM to garbage collect static variables... or Are static fields open for garbage collection?).
Besides you're simply switching between true and false that might be confusing. Instead of using a static variable you might use a non-static one incrementing it, for example, or toast the hash code of the current Activity instance, like Toast.makeText(this,"is " + this.hashCode(), Toast.LENGTH_LONG).show(). By doing this the Activity lifecycle should act as per the documentation.
If I override onBackPressed() ... I always get the Toast 'is false' after I enter the app.
This is more or less similar to what if the system kills your app's process.

From the AOSP Activity class found here:
/**
* Called when the activity has detected the user's press of the back
* key. The default implementation simply finishes the current activity,
* but you can override this to do whatever you want.
*/
public void onBackPressed() {
if (mActionBar != null && mActionBar.collapseActionView()) {
return;
}
if (!mFragments.getFragmentManager().popBackStackImmediate()) {
finishAfterTransition();
}
}
So basically when you call finish, the process is not actually destroyed. You can read more about that here. This means that the memory in your app isn't destroyed, so when you restart your app, the boolean value from before is remembered.
In the case of your overridden implementation, you are explicitly destroying the process, which will clear memory of your activity state, so when you restart the app, the boolean initialization will occur again.

Related

Complete shutting down of Android app when any form of exiting is done i.e. using HOME, SWITCH and BACK button

I'm building an Android app specifically a log in page whereby I'd like the app to completely shut down the app if the user was to exit the log in page in any way i.e. using the SWITCH, HOME or BACK buttons.
The only time the app should not completely shut down should be when user successfully logs in i.e. when the custom created log in button or enter button is pressed.
I've been able to do successfully shut down the app but in shuts down even when the user successfully logs. It isn't meant to do this.
Below is my code for the complete shut down - I took over the onDestroy(), onBackPressed(), finish() and onStop() methods:
// Deal with back button
public void onBackPressed() {
System.runFinalizersOnExit(true);
System.exit(0);
}
// Deal with exiting of app
public void finish() {
System.runFinalizersOnExit(true);
System.exit(0);
}
// Deal with exiting of app
public void onDestroy() {
System.runFinalizersOnExit(true);
System.exit(0);
}
// Deal with exiting of app
public void onStop() {
System.runFinalizersOnExit(true);
System.exit(0);
}
DOES ANYONE HAVE ANY IDEAS REGARDING MY ISSUE
YOUR ASSISTANCE IS GREATLY APPRECIATED
Look here for an explanation of the onStop() method. I suppose you open another Activity when the user has successfully logged in, your Activity above becomes invisible and calls onStop() where you exit your app. So just don't override onStop().
The onStop() method is called each time your activity is made invisible: when your app goes to the background, when the screen is turned off, or when you switch to another activity.
Regardless, what you are trying to do is strongly discouraged.
See this forum thread (Dianne Hackborn being one of the lead developer of the Android Framework).
Force closing your app using System.exit(), Process.killProcess() and the likes has a strong risk of conflict with the Android application lifecycle, and can corrupt its saved state, leading to unexpected and unpleasant behaviors for your users.
A better way is to use the flags FLAG_ACTIVITY_SINGLE_TOP and FLAG_ACTIVITY_CLEAR_TOP when launching your login activity.
This should remove previous activities from application, which would be the same as starting a new application from a user point of view.
Just create a class member boolean mLoggedIn and set it to true when the custom created log in button or enter button is pressed.
Then
#Override
protected void onPause()
{
super.onPause();
if (!mLoggedIn)
{
finish();
}
}

how to use onResume activity in android

I am trying to build an application in which I am applying password on application. when the user left the application and open again then I will ask for the password. for example application is running , the user clicks the home button means left the application then he open it again the the application will ask for the password.
I am creating a password dialog in onResume() method of activity. but the problem is that if the user goes to the next screen and comes back to that screen then also onResume() will execute and will ask for the password. but I don't want this. I want password alert should occur only if he left the application and come again. please tell me any solution where should I write that code.
Give it a try.
Suppose u have A,B activity, create a static variable in A as loggedIn=false;
now suppose u started B from A, on B onbackpress method always make loggedIn=true;
In activity A's on resume method check
if(!loggedIn){
showLogin dialog
}
then assign true again
loggedIn=true;
now if user press home button loggedIn flag will be false and when resumes application the login dialog will be called.
You can create an activity to handle the Authentication process and lets call it (A).
and let your current activities be (B) and (C).
So, this is how it goes:
1- activity (A) is the launcher.
2- add android:noHistory="true" to the <activity> tag of activity (A) in the manifest.xml
3- from (A) you can navigate to (B) then to (C).
4- from (C) you can navigate back to (B) but you'll not be able to navigate back to (A).
You can set variable when home key is pressed
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_HOME)
{
fromHome = true;
}
return false;
};
and set variable false when you navigating from this activity
fromHome = false;
and check flag in OnResume()
Assuming you have two activities a(displaying dialog) and b.When you navigate from one activity a to another activity b. Activity a goes to background. activity b is in foreground. both are put to activity backstack. back stack works like LIFO (Last in first out). When you click back button. activity b is pushed from back stack and activity a is displayed. When activity is paused it has to resume. When you activity is stopped onStart() is called and in succesion onResume() is called. So if you calling dialog in onResume() it iwll get displayed.
Have look at the below links.
http://developer.android.com/training/basics/activity-lifecycle/index.html.
http://developer.android.com/guide/components/tasks-and-back-stack.html.
Note Quoted from the above link: Multiple tasks can be held in the background at once. However, if the user is running many background tasks at the same time, the system might begin destroying background activities in order to recover memory, causing the activity states to be lost. See the following section about Activity state.
Edit
You must implement the onCreate() method to perform basic application startup logic that should happen only once for the entire life of the activity. So display dialog in onCreate() not onResume(). Unless activity is destroyed it is not created again.
It would seem to me that what you are actually trying to keep track of is number of "Started" Activities in your Application: #(Started activities) > 0 would then mean that your application hasn't been put to the background, because if the user presses Home, ALL your App's Activities are bound to have onStop() called.
In order to keep track of the number of "Started" Activities you could introduce a singleton like so:
public class LifecycleTracker {
private LifecycleTracker mInstance;
private int mNrOfStarted;
private LifecycleTracker() {
mNrOfStarted = 0;
}
public static LifecycleTracker getInstance() {
if (mInstance == null) {
mInstance = new LifecycleTracker();
}
return mInstance;
}
public void incrementStarted() {
mNrOfStarted++;
}
public void decrementStarted() {
mNrOfStarted--;
}
public boolean fromBackground() {
return mNrOfStarted == 0;
}
}
Then, in all your Activity's onStart() methods you should first check if you are coming from the background:
if (LifecycleTracker.getInstance().fromBackground()) {
//start authentication
//don't forget to increment after authentication as well!
} else {
LifecycleTracker.getInstance().incrementStarted();
}
And in all your Activity's onStop() methods you should decrement:
LifecycleTracker.getInstance().decrementStarted();

Android onResume from Application

My client wants their app to always show a "WARNING" screen when the application starts or when it awakens from sleep. I've tried creating an onResume() event in my master activity (which every other activity inherits from), but this causes an endless loop:
Activity is called, onResume() is fired
Warning screen fires, causing the calling activity to be paused
User clicks OK to accept the message, returning the user to the prior screen
Activity is woken up
Go to 1
Even if I could get around the endless loop, the Warning screen would fire whenever a new activity loads. This is what I like to call a Bad Thing.
Is there a way to mimic the onResume() event but at the application level rather than at the activity level, so that I can avoid these scenarios but still have the warning pop up on application wake?
Why not just use SharedPreferences.
http://android-er.blogspot.com/2011/01/example-of-using-sharedpreferencesedito.html
Store the time the popup is brought up, and if it was within 5 mins, or something, then don't pop it up.
This will break your loop and not completely annoy the user.
I would write a method to pop the warning and, in onPause, set a global flag. Check that global flag in the onResume, then reset it in your popup method. Simplified pseudo code...
class myApplication Extends Application{
boolean appIsPaused = false;
}
class myActivity Extends Activity{
#Override
protected void onPause(){
appIsPaused = true;
}
#Override
protected void onPause(){
if (appIsPaused){
showPopup();
}
}
public void showPopUp{
if (!appIsPaused){
return;
}
appIsPaused = false;
}
}
You could use an AlertDialog to show the warning message, it would solve your problem.
Else, try to start the warning screen from the onStart or onRestart of your application ?
Here's the android lifecycle if it can help : http://developer.android.com/reference/android/app/Activity.html

OnRestart vs. OnResume - Android Lifecycle Question

My end-goal is to have an application that runs a block of code when it (the application, not the activity) is opened up after being left ( back from home screen, etc... )
According to the Activity Lifecycle, this should be the onRestart() event on a per activity basis ( at least how I interpret it )
Both onRestart() and onResume() are being called whether I am returning to the Activity within the application (back button) AND when the app is called back up.
Given this diagram
I am interpreting it this way:
RED = movement between activities within the application
BLUE = moving to an activity outside the Application
Is my understanding incorrect?
EDIT (Clarifying specific use case)
I'm attempting to use onRestart() to replicate some security logic (PIN Validation) found in onCreate(), but it's being called even when I press the back button inside the application...
My observation is that its hard to tie the lifecycle events to user behavior on the device or emulator. Where your app is paused, if the device needs memory or wants to recover resources, it will terminate the activity, causing onCreate to be called. There is just too many scenarios to build an adequate state machine to tell yourself "how" or "why" your activity was terminated.
The only way I've found to manage this is to create a service to hold the application state and manually manage the state. The problem is trying to use the Activity state to manage the application state. The Activity design seems to have limitations that just make it a poor choice for achieving the goal you've stated.
That would be because when unless your are using Fragments each "screen" in your application is a new activity, when you click the back button it restarts the activity of the page before it.
If I am understanding what you want to do correctly you want to put your code on onCreate, not onRestart.
SEE COMMENT THREAD FOR ANSWER
Here is how to do this:-
Have a base activity that all your activities are derived from.
Add in to the base activity:-
int nAppState;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
nAppState = 0;
.
.
}
protected override void OnStop()
{
AppState();
base.OnStop();
}
public static int IMPORTANCE_BACKGROUND = 400;
protected override void AppState()
{
ActivityManager am = (ActivityManager)GetSystemService(Context.ActivityService);
IList<ActivityManager.RunningAppProcessInfo> list2 = am.RunningAppProcesses;
foreach (ActivityManager.RunningAppProcessInfo ti in list2)
{
if (ti.ProcessName.ToLower() == "com.mycompany.myapp")
{
nAppState = ti.Importance;
break;
}
}
}
protected override void OnRestart()
{
base.OnRestart();
if (nAppState == IMPORTANCE_BACKGROUND)
{
// Show a log in screen
RunOnUiThread(delegate { StartActivity(new Intent(this, typeof(LoginAppearActivity))); });
nAppState = 0;
}
}
Please note that this is in Mono C#, it will be the same code for Java, I'll leave it up to you to convert it!!
Yes, your assertions for red and blue are correct.
However, note the alternate pathway from onPause() and onStop(). Process being killed for memory reasons is a) out of your control and b) imperceptible to you if you only use onRestart() to detect "coming back" to the activity.
You have an option to avoid the previous activity by avoiding/removing the activity to come in Stack by setting some flag before calling the startActivity(intent):
intent.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NO_HISTORY);
This will avoid the present activity to get called on back press. Alternatively you can also ovverride the onBackPressed() method of the current activity.

Android: Proper way to handle Activities

Firstly, I'll explain the situation I'm in with my applicaton before I get onto the main question I have. Firstly, I want to find out if the way that I am handling activities is a proper way of handling activities, as I feel it might well not be which in turn is causing my problems.
Currently I have 3 activities setup. Main.class, Login.class and Display.class. The Main activity doesn't have any UI associated with it at all, it launches other activities. So, the first activity that get's launched when the app is launched is the Main activity, and it goes and reads from a SharedPreferences store whether or not the user is logged in. If they aren't logged in, it will open up Login.class, if they are logged in, it will open up Display.class (both of which have UI's associated with them). It uses startActivityForResult().
Because of the way I decided to go around working with activities, I needed to override the back button on both Login.class and Display.class: otherwise, it will go back to the Main.class, and re-run the launch activity sequence, which will inturn re-launch the activity that was just running (therefore the back button is useless). So, I overrode the back button to send back an integer of -1 and then finish() the current activity. I've overridden the onActivityResult(int, int, Intent) for Main.class, and if it receives a -1, it will finish() as well, instead of trying to re-launch the activity.
Now, that all worked for how I wanted it, though I have a feeling that it's the completely wrong way to handle activities, and it's causing problems for me later down the track.
In my application, I decided I wanted to put an app widget and a notification in, both of which will open the app when clicked (via an intent that launches Main.class). I got it all working so that it would do that, though it seems as though starting a new Activity causes problems.
What happens is basically, if the application is already running, and someone pulls down the notification and clicks on it, it will open up a new activity of the same application. This is problematic, especially with the way I handle back buttons: When you open a new one, and press the back button, it will close the new one (as expected,) but will then take you back to the OLD one. You can then press the back button again and exit it, but obviously I don't want to release an app that needs the back button to be pressed twice. What is the best way to sort this problem out? I'm happy to completely change the way I handle activities if that's a major part of the problem.
Thanks,
Josh.
Actually how you set a -1 (which is fine) and then call finish() is perfectly acceptable. I forget the property values, but if you want only a single instance of an Activity there is a way to set that in the manifest ... Task affinity or one of those values - you'll have to look it up.
As an alternate, you can override the Application class and use your own to manage application state. Think of it as a singleton tracker for the main Activity .. "if it already exists use that one, otherwise create a new instance" kind of thing. When creating your main Activity set a reference in your newly extended Application class (make sure you null this out when main is shutting down), then check to see if a reference is available when onCreate() fires again ... if there is a reference already there use that instance of Main .. if not, proceed as normal and set it.
Food for thought ...
I have a feeling that I don't fully understand the depth of the problem, so please forgive me if I'm saying something that you already tried.
But couldn't you remove Main and launch Login from Display? I mean, makes more sense. That's what I do here all the time when I need accessory activities that must fill data for the main activity (which is obviously Display in your case). And when you return from login, you could do all the checks you need. You could allow, say, a "read-only" Display, you could provide a dialog warning...
You would save a lot of trouble and useless code indeed (most of the result/intent spaghetti).
A feasible approach is to have a class extending Application and a couple of Activitys bound to it. A private boolean logged value can determine whether the Display needs to call LogIn to foreground.
public class MyApp extends Application {
private static boolean logged = false;
private Activity logInActivity;
private Activity displayActivity;
public void onCreate () {
super.onCreate();
}
public void setDisplayActivity (Activity act) {
displayActivity = act;
}
public void setLogInActivity (Activity act) {
logInActivity = act;
}
public void finishActivities (Activity act) {
activity.finish();
}
public void setLogged (boolean logged) {
this.logged = logged;
}
public boolean isLogged () {
return logged;
}
public Activity getLoginActivity() {
return logInActivity;
}
public Activity getDisplayActivity() {
return displayActivity;
}
}
class Display extend Activity {
private MyApp app;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (MyApp) getApplicationContext();
if (!app.isLogged()) {
// start LogInActivity. After log in, it will call app.setLogged (true);
}
else {
// continue with Display;
}
}
}

Categories

Resources