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
Related
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.
I declare my variable 'shake' as a global variable, then I created new object inside the oncreate, then I call this :
#Override
public void onPause() {
super.onPause();
shake.cancel();
}
my phone can still vibrate although home button is pressed! I tried onStop(), same doesn't work..
my app is like this : countdown 10 sec, after that vibrate.. but the problem is onPause cannot be call so the user may feel where's the vibrate come from if it's set 2 minutes on the countdown ticker.. help!
Since I can't see the rest of your code, I'm gonna assume a few things.
Assumption #1
If you have your activity open, and the countdown starts and expires after 10 seconds, your phone vibrates (with your activity still open). If you go to home screen, the vibration stops.
Assumption #2
You have your activity open, and the countdown starts. Before the 10 second expires, you go to home screen. Your activity is not visible, but the phone starts vibrating soon.
If this is what you are seeing, it's the correct behavior. The problem is that in the 2nd case, your shake.cancel() from onPause() is called when you go to the home screen, before it actually starts vibrating. shake.cancel() can only cancel if it's already vibrating.
If that's what you are trying to fix (I can only assume since I can't see the rest of your code), you can try this:
private boolean mAllowShake = false;
#Override
public void onResume() {
super.onResume();
mAllowShake = true;
}
#Override
pulic void onPause() {
super.onPause();
mAllowShake = false;
shake.cancel();
}
// wherever you are calling the shake.vibrate()
if (mAllowShake)
shake.vibrate();
This way, when your activity is not visible and your timer goes off, since mAllowShake is false, it won't actually vibrate.
If that's not what you are trying to fix, please update your question with more code and description of your exact use case. Hope it helps!
A snippet from the Android Activities document(scroll down to the "foreground lifetime" line) says :
An activity can frequently transition in and out of the foreground—for
example, onPause() is called when the device goes to sleep or when a
dialog appears.
I don't quite understand this. Under what circumstances should this happen? Is onPause() called only if the context of the dialog in question is different from the activity on top of which the dialog is to be displayed?
EDIT: Adding code sample to illustrate my doubt in detail
Going by the above-mentioned quote from document, should my activity's onPause() method get called when the AlertDialog (or just the Dialog) in the following code gets displayed? Should I see the "onPause called" log entry when the dialog is displayed?
But I don't see that happen. And it shouldn't either, if I have understood the Android life cycle correctly! So, what's the document pointing at then?
public class LifeCycleTestActivity extends Activity {
private static final String TAG = "LifeCycleTest";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn = (Button) findViewById(R.id.button1);
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "onClick");
AlertDialog dialog = new AlertDialog.Builder(LifeCycleTestActivity.this).create();
dialog.setMessage("You Clicked on the button");
dialog.setTitle("Dialog!");
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
dialog.setCancelable(true);
dialog.show();
/*
Dialog dialog = new Dialog(LifeCycleTestActivity.this);
dialog.setTitle("Dialog!");
dialog.setCancelable(true);
dialog.show();
*/
}
});
}
#Override
protected void onPause() {
Log.d(TAG, "onPause() called");
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume() called");
}
}
onPause() is called when your activity is no longer at the top of the activity stack. A Dialog by itself is not an Activity, so will not replace the current Activity at the top of the stack, so will not cause anything to pause.
A dialog (lower-case) does not need to be implemented by a Dialog class, however. For example, it is not uncommon to implement one with an Activity whose theme is set to that of a dialog. In this case, displaying the dialog-as-an-Activity will cause the new Activity to be on the top of the stack, pausing what previously was there.
I've been doing quite a lot of code with dialogs, including the AlertDialog that you mention, and I've also tried to check if onPause() is being called on the activity when the dialog pops up, but thus far my conclusion is that the activity simply keeps running and that onPause() is not called.
Not sure if it helps, but at least you now know that there are others who experience what you're experiencing :-)
Its wrong that activity remains no longer at top of activity stack in onPause phase.
Condition an activity to be onPause state -
Activity partially visible e.g. dialog on activity.
The Activity object is retained in memory, it maintains all state and member information, and remains attached to the window manager.
e.g Home button pressed causes activity to go in onPause(). Still at top of stack.
In fig 1. Activity3 will be destroyed and removed from top stack
In fig 2. Now Task A goes to background but Activty X still on top of stack . If you override onPause() method int this state
Figure 1. A representation of how each new activity in a task adds an item to the back stack. When the user presses the Back button, the current activity is destroyed and the previous activity resumes.
Figure 2. Two tasks: Task B receives user interaction in the foreground, while Task A is in the background, waiting to be resumed.
I think I remember reading in an earlier version of the Android Lifecycle that onPause was called when none of the activity is on display. i.e. if a bit of your activity is still visible under a popup, onPause will not be called.
Maybe some other experts can vouch for this behavior?
In my slightly weird experience onResume gets called with dialog.setCanceledOnTouchOutside(true); but onPause never gets called.
That being said, I think the documentation might focus on system dialogs (e.g. low on battery).
#hackbot
onPause() is called when your activity is no longer at the top of the activity >stack. A Dialog by itself is not an Activity, so will not replace the current >Activity at the top of the stack, so will not cause anything to pause.
everything depends on implementation...
what is a Dialog ? is a Window added to Display by WindowManager///
so the window when it shows is on top of everything .... (Z order)
what is activity... is "thing" that also creates its window....
when a dialog is shown or it's window comes visible on top of an existing activity, then it overrides partial the activity window so existing activity will move to partially invisible state and you will get call to onPause() from ActivityThread.
but to be sure we also need to consider here a one think...
the state of window if is a standalone window shown on top or it is a child window and a parent of it is a activity window....
so when we know
the Window.LayoutParams (FLAGS) we use to add
and what IBinder is used for the Window to show
we will khow how the activity will behave when windows are shown each over other .. as each winndow has a callbacks they are used by activity or dialog to manage their states...
involved components:
android.os.IBinder
android.view.Window
android.view.Window.Callback
android.view.WindowManager
android.view.WindowManager.LayoutParams
android.view.Display
btw:
if you want to know the windows on screen [ applicable only for the process you own - as window belongs to process and those are Sandboxed - each processs is a separate JVM strictly saying "ART" ] you can use a replection see :
android.view.WindowManagerImpl
android.view.WindowManagerGlobal
onPause() is called every Time when an Activity goes background and Dialog or other Activity comes foreGround. This is done to give first priority to something with which the user is interacting. e.g: assume you are in homescreen (which in turn is an activity) of an application, the homescreen is said to be in foreground. and when you go to next screen by pressing some button or a dialog appears the next screen/Activity/Dialog comes to foreGround and homecreen goes to backGround, which just means homeScreen's onPause() method got called.
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.
It was my understanding, obviously wrong, that onPause() is called whenever the back button is pressed? Now in my code I've put this onPause() event:
#Override
protected void onPause(){
super.onPause();
if(!_END_GAME){
Builder _alert = new AlertDialog.Builder(this)
.setMessage("onPause, with game NOT over!");
_alert.setNeutralButton("OK.",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
arg0.dismiss(); // Kills the interface
System.runFinalizersOnExit(true);
finish();
}
});
_alert.setTitle("Your Score!");
_alert.show();
}
}
Now the problem is, the dialog does not launch what-so-ever, and then the code errors out. I put the dialog there to try to visualize where the onPause() was called and help me debug some other variables and such. Yet like I said it never even gets shown. Any ideas why this would be? Is there a function that is launched prior to onPause() when the back button is pressed? Thank you in advance for any info.
onPause will always be called when your activity is no longer in the foreground, that's guaranteed. Maybe your _END_GAME is not false? Add a debug log output to your onPause method, you'll see that it always gets called.
I should note though that displaying a dialog during onPause is extremely bad form - the user is trying to get rid of your app (could even be because of an incoming phone call). You DO NOT want a dialog then. That goes against the Android design.
In fact, the Android OS will simply short-circuit your app if you try to do lengthy shenanigans in onDestroy or onPause. Basically, if those get called, you're supposed to disappear quietly.
If you really want to intercept the back button, you can check for the button like Ted suggested, but keep in mind that your app can go to the background in many other ways - home button, selected notification, incoming phone call, etc.
You should check for the back button by overriding onKeyDown, not testing in onPause. onPause gets called whenever your activity is no longer in the background leaves the foreground; it is not necessarily finishing. (You can check isFinishing() for that.) See here for more info on handling the back key.
onPause is getting called, and your dialog is showing, just for a tiny split-second before Android finishes your app. Put log statements in there if you want to watch what is going on.
If you want to show a dialog when the back button is pressed then the easiest way (works on Android 2.1+) is to override the onBackPressed method in your activity
#Override
public void onBackPressed() {
if (gameRunning) {
// show dialog
} else {
// exit
super.onBackPressed();
}
}