Having an issue with lifecycle events and need some help if possible. I have read the other posts with similar issue and error however, still stuck .... Explaination may be a little long.
I have an App that requires the user to login when any 'backgrounding' event occurs (App switch, screen sleep, phone call and so on). Previously I was using the method of 'getRunningTasks' to check is my app is no longer top of stack to trigger the login flags and activity. I am now using a suggested timer method via onPause / onResume in my Application class which works great.
Now the issue at hand, and the questions:
App into background by Device Home button.
App is in the background, all my checks and flags are properly set. When I relaunch the app (Icon press), it loads the last activity, then from the onResume function - performs a startIntent for the login activity - based on the flag set in the timer event. The user logs in, and returns to the activity they were on (Stock List). All Good.
App into background by App Switch
timer event fires and all login flags are properly done as previous example.
--> Click 'device Home'button, then App icon --- App loads properly and loads the Login Activity, users logs in -- all good.
Problem area
2b. App into background by App Switch --> Example, Android pull down menu, jump to device settings - then return to app via the device back button
timer event fires and all login flags are properly done as previous example.
--> Click device 'return/back' button to return directly to the app.
User goes to Login screen, clicks 'Login' button - gets app crash due to :
java.lang.RuntimeException: Performing pause of activity that is not resumed
followed by
java.lang.RuntimeException: Performing stop of activity that is not resumed
My own system Logs in the lifecycle events all seem to fire in the appropriate order.
Basic overview from app re-launch:
-> DashBoard onResume
-> check for login flag and call intent for Login activity
Dashboard onPause
Login onResume
Login - user login hit button
Login onPause --- activity finish
Return to Dashboard onResume
-> Load details
The only diff is in the last senario ... the DashBoard onPause happens after the Login onResume. Not sure if this is the problem, or an Async delay issue with the lifecycle events.
My Manifest includes:
android:minSdkVersion="10" --- Maintaining old API due to clients with older devices
android:targetSdkVersion="19"
Any help or suggestions on what may be out of whack here? or how to avoid this error.
Thanks
--
Some code:
From BaseActivity.java
~~~~~~
private AppPreferences mAppPrefs;
protected MyApplication mMyApp;
#Override
protected void onPause() {
super.onPause();
//start timer
mMyApp.startActivityTransitionTimer();
}
#Override
protected void onResume() {
super.onResume();
//update current Activity Name
mMyApp.setCurrentActivity(this);
mMyApp.stopActivityTransitionTimer();
mAppPrefs = new AppPreferences(getApplicationContext());
if (mAppPrefs.getAppDestroyed() == true
|| mAppPrefs.getExitType() == AppPreferences.EXITTYPE_FULLEXIT) {
// we want to fully exit/close the app
this.finish();
} else {
if (mAppPrefs.getForceLoginState() == true) {
//~~~~ process login events
Intent intent = new Intent(this, LoginScreen.class);
startActivity(intent);
return;
}
}
}
DashboardActivity extends BaseActivity
{
#Override
protected void onPause() {
super.onPause();
if (((MyApplication)this.getApplication()).getProcessingLogout()){
// if we are already processing a login, do not continue here
return;
}
// ~~~ activity specific onPause event such as unregisterReceivers
((MyApplication)this.getApplication()).startActivityTransitionTimer();
}
#Override
protected void onResume() {
super.onResume();
if (((MyApplication)this.getApplication()).getProcessingLogout()){
// if we are already processing a login, do not continue here
return;
}
// ~~~ activity specific onResume events such as registerReceivers
}
}
Related
In Android, I want to show Splash Screen only when the app is starting fresh and not when it is working on the background and resuming by the user.
So after reading here in Stackoverflow and over the web, I have added finish() before starting my home activity:
#Override
protected void onPostExecute(String s) {
finish();
Intent appStarterIntent = new Intent(StarterActivity.this, HomeActivity.class);
StarterActivity.this.startActivity(appStarterIntent);
}
But Still when I (returning) opening the app from the background I see the Splash screen...
How can I make the plash screen apprear only on fresh start of the app?
Save value 'isFreshStart' into SharedPreferences and start app only if there is no saved value. Remove this flag in onTerminate() method of Application class.
The point is, that Application instance gets destroyed when app is closed coimpletely, so you will know if it is a fresh start.
I have an application, which starts with a SplashScreenActivity. Afterwards, a LoginActivity is shown, or if the user is already logged in, a MainActivity is shown. If the application is already running, SplashScreenActivity is dismissed with the following
//SplashScreenActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Adding this check for following cases
if (!isTaskRoot())
{
String intentAction = getIntent().getAction();
if (getIntent().hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
finish();
return;
}
if(getIntent().getCategories().contains(GCMIntentService.INTENT_CATEGORY_GH_NOTIFICATION)){
finish();
return;
}
}
Problem occurs
If I start the application from another activity like PlayStore, it resumes at the right activity if already running. This is the Intent I'm using to reproduce within a second app
//AnotherApplication.apk
Intent launchIntent = getPackageManager().getLaunchIntentForPackage("my.package.name");
startActivity(launchIntent);
However, this action is somehow breaking the Backstack. Instead of closing the application on backpress in the MainActivity, it restarts the application.
//MainActivity.class
#Override
public void onBackPressed() {
if (getNavDrawerMain().isDrawerOpen()) {
getNavDrawerMain().closeDrawer();
} else {
closeApp();
}
}
protected void closeApp() {
if (doubleBackToExitPressedOnce) {
//super.onBackPressed(); //i tried both, but behaviour is the same
finish();
return;
}
this.doubleBackToExitPressedOnce = true;
new Handler().postDelayed(new Runnable() {
#Override
public void run()
doubleBackToExitPressedOnce = false;
}
}, 500);
}
I used breakpoints and found out that MainActivity:onDestroy() get called, but instead of resuming application to the HomeScreen, it always restarts and I don't know why.
I tried the following:
- Used different launchmodes like singleTask and singleInstance, but it didn't make any difference. onNewIntent is called, but if i call finish, HomeActivity restarts
- as commeted below, i tried moveTaskToBack(true), but Activity is restaring too (and we really want to close the app instead of moving it to the BackStack)
Try with moveTaskToBack(true); instead of finish(); to close the app. It will then go to OnRestart() and then OnStart()->OnResume() (and won't go to OnCreate).
And make sure you don't have the "Don't keep activities" marked at Developer Options in your Android Settings (destroy every activity as soon as the user leaves it).
Trying adding this flag to your intent starting your app: RESET_TASK_IF_NEEDED, URL=http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
What it does:
If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task.
You may also use:
http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_REORDER_TO_FRONT
What it does:
If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought to the front of its task's history stack if it is already running.
Which one you use depend on the desired end result.
If you can't control who starts you you need to set or launch mode to single task or single instance.
Described here:
http://inthecheesefactory.com/blog/understand-android-activity-launchmode/en
The interesting part:
singleTask
This mode is quite different from standard and singleTop. An Activity with singleTask launchMode is allowed to have only one instance in the system (a.k.a. Singleton). If there is an existed Activity instance in the system, the whole Task hold the instance would be moved to top while Intent would be delivered through onNewIntent() method. Otherwise, new Activity would be created and placed in the proper Task.
I have an app that it password protected to stop unauthorised access to data.
What I need to ask is:
when app is closed using the home button, then reopened I need it to go to the LoginActivity, I think I have sorted this using android:launchMode="singleTask"
But if I press the back button it takes me back to the menu without the need for logging in again - so it bypasses the password?
Can you please help?
On your activity, before it goes to background by pressing home, set a flag that the user has logged out or clear the session.
Then onResume of your activity, detect if the flag for user is still logged in or session is still valid. If not valid. then just send an intent to open your login activity.
So even you press back, that activity that resumed will validate if the user is still logged in or the session is still valid.
Create a custom application class with a flag indicating whether the user is logged in.
public class MyApplication extends Application {
boolean loggedIn;
}
In your activity you can then check if the user is logged in. If they aren't, return them to the login activity.
protected void onCreate(Bundle savedInstanceState) {
MyApplication app = ((MyApplication) getApplicationContext());
if (!app.loggedIn) {
Intent intent = new Intent(this, LoginActivity.class);
//go to old activity
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
}
I have an application that uses Urban Airship for push notification. When a notification arrives and the user clicks on it, activity A in my application should open and do something.
I've installed the BroadcastReceiver as is shown in the docs, and it's almost working.
When my app is in the foreground I don't let the user see the notification at all, and just handle it automatically.
When my app is not running at all, the activity opens up just fine.
When my app is in the background (which always happens when A is the top activity), a second instance of Activity A is created.
This is, of course, a problem. I don't want two A activities, I just want one of them. Here's the relevant BroadcastReceiver code:
#Override
public void onReceive(Context ctx, Intent intent)
{
Log.i(tag, "Push notification received: " + intent.toString());
String action = intent.getAction();
int notificationId = intent.getIntExtra(PushManager.EXTRA_NOTIFICATION_ID, -1);
if(action.equals(PushManager.ACTION_NOTIFICATION_OPENED))
{
Intent intentActivity = new Intent(ctx, ActivityA.class);
intentActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
UAirship.shared().getApplicationContext().startActivity((intentActivity);
}
}
UPDATE:
I tried to bypass this bug by calling System.exit(0) when the user presses Back on Activity A. The process ended, but then it was restarted immediately! My BroadcastReceiver is not called again in the second instance. What's happening?
UPDATE 2:
#codeMagic asked for more information about the app and activity A.
This app lets its user review certain items and comment on them. Activity A is started when the app is launched. If the user's session isn't valid any more, a Login activity is started. Once the user logs in, activity A becomes active again. A only has a "No items to review" message and a "Try now" button.
When the user logs in, the server starts sending push notifications whenever a new item is available for review. When the app gets the notification, activity A accesses the server and gets the next item to review. The item is shown in activity B. Once the review is submitted to the server, activity B finishes and activity A is again the top activity.
The server knows when a user is reviewing an item (because activity A fetched it), and doesn't send push notifications until the review is submitted - meaning a notification can't come if the user isn't logged in or if the user is viewing activity B.
While I agree there is a subtle race condition here, it is not causing the problem I'm seeing - in testing I am 100% positive there's no race condition - the push notification is only sent after Activity A becomes active again.
The solution was to add a launchMode='singleTask' to the activity in AndroidManifest.xml . As a result, instead of a new activity, onNewIntent of the same activity instance is called.
You can use one of several Intent Flags. FLAG_ACTIVITY_REORDER_TO_FRONT being one of them. This will bring the Activity to the front of the stack if it is already in the stack and if not then it will create a new instance. I believe you will still need FLAG_ACTIVITY_NEW_TASK if you aren't calling it from an Activity
Intent.FLAG_ACTIVITY_CLEAR_TOP should also work. But this will clear any other Activities on the stack. It just depends on what other functionality you need. Look through the Intent Flags and see which of these will work best for you
There are multiple scenarios when this could happen. One of them can be handled this way. Please see my answer here: https://stackoverflow.com/a/44117025/2959575
Ok, two notes on this :
You can register a broadcast receiver via the manifest so it is independent of any parts of your app. and use a Singleton pattern (keep a static reference to your activity somewhere in your app) that way you can check if their is an activity viewing or not and process accordingly.
// your activity A
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
myActivityReference = this;
}
public void onPause() {
super.onPause();
if (isFinishing()) {
myActivityReference = null;
}
}
or you can keep everything as it is and use activity lunching modes flags in your manifest such as singleTop, singleInstance ... etc. take a look here android activity lunch modes
I have a question about the android lifecycle. This is my onCreate method of my main application:
#Override
public void onCreate(Bundle b) {
super.onCreate(b);
decode = new Decode(this);
adjustToDimensions();
setLoadingScreen("Zorgdossier wordt geladen...");
initReceiver();
startCommunicationWithServer();
}
I set the screen to a loading screen, initialize a broadcastReceiver and send a message to the server.
Now, when I press the home button and start my application again I always want to start this activity so I've added
android:clearTaskOnLaunch="true"
To the manifest.
But now I want the startCommunicationWithServer() to be executed when the user starts the application again. This method sends a message to the server, and the server on the other hand sends a message that I receive in the receiver.
With this message I decide which screen to show.
I've tried to put it in the onRestart method:
#Override
protected void onRestart() {
Log.d("MainActivity", "activity restarted");
startCommunicationWithServer();
super.onRestart();
}
But this activity is shown multiple times troughout the application, so the onRestart method is executed each time. But I only need the startCommunicationWithServer() to be executed at the start of the application.
So long story short, how can I call the startCommunicationWithServer() method when the user exits and subsequently starts my application?