I googled but not find helpful information about this. If I open app and go through these activities A-B-C-D, app has 4 activity in back stack. Android can kill process, what he actually does, after restoring app I see activity D, so how I can handle this behavior, how to check how many real activity I have in back stack.
I tried:
ActivityManager mngr = (ActivityManager) getSystemService( ACTIVITY_SERVICE );
List<ActivityManager.RunningTaskInfo> taskList = mngr.getRunningTasks(10);
Log.d("myTag",taskList.get(taskList.size()-1).numActivities+"");
Or
ActivityManager mngr = (ActivityManager) getSystemService( ACTIVITY_SERVICE );
List<ActivityManager.RunningTaskInfo> taskList = mngr.getRunningTasks(10);
if(taskList.get(0).numActivities == 1 && taskList.get(0).topActivity.getClassName().equals(this.getClass().getName())) {
Log.i(TAG, "This is last activity in the stack");
}
But it show the same number before and after killing process.
I want to redirect user to actviity A if my app was terminated, and back pressed event will close app.
You should handle with this using onSavedInstanceState.
Check this image:
The guideline says:
The two ways in which an activity returns to user focus with its state intact: either the activity is destroyed, then recreated and the activity must restore the previously saved state, or the activity is stopped, then resumed and the activity state remains intact.
So, you should save the instance state and restore this at onRestoreInstanceState then you can do what you want based on previous state of your activity.
Check this link for reference: http://developer.android.com/guide/components/activities.html#ConfigurationChanges
Hope that it helps.
Related
I have a flow in my application like this:
For new users:
Splash Screen --> Login Activity --> Home Activity
For already registered users:
Splash Screen --> Home Activity
Basically the Splash Screen has an if else to decide which activity to go to. Once a first time user logs in, his status is saved in a preference variable for the splash screen to decide next time not to open the login activity.
Now the situation is that. If a new user logs in and goes to the home activity, and then logs out. He is redirected to the Login screen which is pretty much what should happen. But, in case an existing user opens the app, he is shown the Splash screen and directly moved to the Home Activity. Now if the user logs out, he gets out of the app. This happens because the Login Activity does not have any instance created and thus finishing the Home Activity finishes the whole app. Logout actually finishes the Home Activity, naturally the last active activity should open up. Which is not happening.
What I want to do is that, I want to implement a logic which will check that the Login Activity is available or not. If its available then finish() will be called else the Login Activity will be called via intent.
Please tell me how to achieve this.
P.S: My app uses a custom theme with a customized action bar. If I call finish and Intent together or I use flags to clear existing activities then there is a weird transition effect which shows the black standard action bar for a split second thus creating a bad user experience.
Now if the user logs out, he gets out of the app. This happens because
the Login Activity does not have any instance created and thus
finishing the Home Activity finishes the whole app.
If i understood your question, why dont you just call the Login Activity manually after user click a logout button?
Its what i always did with apps that have flow like yours
when user login finish login activity and start home activity.
when user logout finish home activity and start login activity
You always can call Login Activity via intent. If the activity is available, android will show this activity. Else android will create new activity automatically.
Actually that's why we use intents to show activity instead of creating activityes manually. System catches this intents and does all dirty work.
EDIT:
Hmm, but wouldn't you have the transition problem anyways? (If you were already logged in, and then log out - using intent/finish() you will have the same black action bar issue no?)
Maybe consider following ( I actually did this in my app):
Merge Splash screen and Login into one activity and depending whether you are logged in - display the login fields or proceed to home screen. Then you have a out of box consistent stack of activities regardless of use cases and no mambo-jumbo with do I already have this or not.
I can't comment because of I lack 4 rep, so I'll post as answer here:
I think #Blaze Tama is right. You can also use FLAG_ACTIVITY_CLEAR_TOP on intent to avoid stack flow problems:
From docs:
If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.
Always start Login activity and start Home activity right away if the user already logged in.
In the Splash Screen activity
Intent intent = new Intent(this, Login.class);
If (user already logged in)
{
intent.putextra("Logged in", true);
}
startActivity(intent);
In the Login activity
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent != null)
{
if (intent.getBooleanExtra("Logged in", false))
{
startActivityForResult(new Intent(this, Home.class), requestCode);
}
}
else
{
// The existing code here
}
}
In Home activity send back a code to indicate if the user logged out or just BackPress. If BackPress finish this Login activity.
This question already has answers here:
How to exit from the application and show the home screen?
(22 answers)
Closed 5 months ago.
So, I'm trying to create an app which have a function like an alarm clock for Android phones, it will alert the user at their chosen time. When the alert screen show up, the user has an option to push a notification or snooze, and the app will push a notification and then close.
However, when I click the snooze or the notification button, the app did not close. Instead, the app stop the alarm sound and minimized (just as when the user touch the home button). If I touch the recent app, I then can open the alert screen again.
Edit: I know that there're some codes such as System.exit() or killProcess, since those code are not recommended, I prefer avoid using them.
The reason I ask is because I tested the real clock app that come with my phone (4.3), and the it's alert screen will close after the I press the snooze or dismiss button. So there must be a way for me to do the same, right ?
Answer
Okay, so as Sagar Pilkhwal explained below, and after reading others related problems, I found out that there's no "good" method to close you app by codes, you have to leave that option to the users or OS. Unless you want to use System.exit or killProcess, but they're bad ways to force your app to close.
Sagar Pilkhwal also have a alternatively method to this problem, if you don't want your alert screen show up in recent app, you bring up another activity.
However, as for this case and Alarm Manager in general, open MainActivity will lead to nasty stuffs, since MainActivity is when we handle Alarm events. Then I found out another solution, if you don't want your alert screen show up in recent app, simply add
android:excludeFromRecents="true"
to your Alert.class Activity in the Manifest, or add Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTSto the intent used to start Alert.class.
This will lead to other problem, as when the user press the Home button, the alarm won't turn off, and the user can't open recent app to access the alert screen (he'll has to open the app to do so). I fix this problem by try to detect the home button pressed event. Detail answer are below.
well im thinknig.
System.exit(0);
maybe? im new to android thou..but i sometimes use it..
Put the following to Manifest, in your Alert activity :
android:excludeFromRecents="true" //this will make the Activity be exclude from recents list.
You can also add android:launchMode="singleInstance" and android:taskAffinity="" (if you know what they do)
In the Alert class, use this to detect home button pressed event:
#Override
public void onPause() {
if (!isFinishing()) {
createNotf(); //Handle home button press here.
}
super.onPause();
}
Use this to handle back button pressed: (You have to have this code if you want to detect home button press using isFinishing();
#Override
public void onBackPressed() {
//Handle BackButton event here.
}
For my case only, when the user press home button, I will create a notification, so he can either click on the notification or open the app again to access to Alert Screen.:
private void createNotf() {
Intent screenIntent = new Intent(MyAlert.this, MyAlert.class);
screenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
screenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
PendingIntent pIntent = PendingIntent.getActivity(MyAlert.this, MainActivity.SEND_ALARM_CODE, screenIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Notification.Builder ntfBuilder = new Notification.Builder(this)
.setAutoCancel(true)
.setContentTitle("ALARM_RUNNING")
.setLargeIcon(bitmap)
.setSmallIcon(R.drawable.ic_launcher)
.setContentText("CLICK_TO_OPEN_ALERTSCREEN.")
.setContentIntent(pIntent);
NotificationManager myNotfM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
myNotfM.notify(999, ntfBuilder.build());
}
When the Alert stop, that notification will also be clear:
#Override
public void onDestroy() {
super.onDestroy();
NotificationManager mntfM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mntfM.cancel(999);}
System.exit(0);
That right there will close your application out leaving nothing running in the background.However,use this wisely and don't leave files open, database handles open, etc.These things would normally be cleaned up through the finish() command.
I personally HATE when I choose Exit in an application and it doesn't really exit.
I've got a couple of activities and an intent service which handles GCM incoming messages.
Right now for every push, I'm sending a Notification, and after the user clicks it, he is redirected to appropriate screen.
I would like to alter this behavior that if the app is visible (any activity is in the foreground), instead of the notification a dialog message is shown (with appropriate action).
Any idea how to implement it?
I have 2 ideas but none of them is perfect:
Keep track of every activity in the application, if the activity is visible, don't show notification, but sent an intent to the activity (not nice solution)
register/unregister the second broadcast receiver in each activity's onResume/onPause, "catch" the incoming GCM broadcast (I'm not sure if it is possible).
Any other solutions?
A possible solution (idea 1):
To detect whether your app is running back- or foreground, you can simply set a boolean in onPause/onResume:
#Override
protected void onResume() {
super.onResume();
runningOnBackground = false;
}
#Override
protected void onPause() {
super.onPause();
runningOnBackground = true;
}
When you start a new intent from an notification this method gets called: (if you are using singleTop), with the boolean you can determine what to do in the onNewIntent method.
#Override
protected void onNewIntent (Intent intent){
if(runningOnBackground){
//do this
}
else{
//do that
}
}
Hope it helps!
I didn't test it, but the docs say you can get the number of running activities per each task.
Try to find your application's task among currently running tasks:
ActivityManager acitivityManager = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
// Get the top of running tasks, limit by 100
List<RunningTaskInfo> tasks = acitivityManager.getRunningTasks(100);
for (RunningTaskInfo taskInfo : tasks) {
if (YOUR_PACKAGE_NAME.equals(taskInfo.baseActivity.getPackageName())) {
if (taskInfo.numRunning > 0) {
// Show dialog
} else {
// Show notification
}
break;
}
}
Google added a note on getRunningTasks():
Note: this method is only intended for debugging and presenting task management user interfaces. This should never be used for core logic in an application, such as deciding between different behaviors based on the information found here. Such uses are not supported, and will likely break in the future. For example, if multiple applications can be actively running at the same time, assumptions made about the meaning of the data here for purposes of control flow will be incorrect.
So use it at your own risk.
Also check if GCM broadcasts are ordered. If so, you can "override" your default BroadcastReceiver with the other ones in each Activity. Just play with the priority of IntentFilters. When the BroadcastReceiver with higher priority receives the message, it can abort it's further propagation. For your application this means that when some Activity is running, it registers the receiver which shows the dialog and aborts broadcast. If no activity is active, then your default receiver shows the notification.
In Android, how does one know an app has been started. I want to detect all apps installed when it starts, and range apps' priority according to its using times. Is there a solution to suggest to do this?
I know using broadcast, but is there some Intent send out from ActivityManager when app had been started, and how to detect this Intent in code? Any other solution is welcome, too.
First part:
If you know the package name of your app, try this (put this following snippet in the onCreate method of your app):
ActivityManager am= (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
Then,
boolean exit = false;
while(!exit)
{
List<RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
if(componentInfo.getPackageName().equals("Your package name"))
{
//Do your work here
exit = true;
}
}
When you start your app, this will be put into componentInfo. The taskInfo.get(0).topActivity will return the activity in the foreground. Hence you can know that your app has been started by comparing package using the second code snippet.
Note:Put this second code snippet in an Asynctask so that the checking of whether the app has started can be done in the background.
Second part:
To get the priorities, I think you can do it by checking the list TaskInfo which will contain all the running apps.
My application is quite simple, I have a few activities (a LoginActivity which is the launcher and the main activity). Then I have some other activities and finally an activity RouteActivity which launches a service.
The LocationService extends Service and startForeground with a notification. The service just starts a LocationListener and register every new GPS location point.
The service appears to work just fine, if I touch the notification icon it bring me back to my application activity from where it was started (not from the Login activity).
Now here is the problem, if I touch the application icon (on the Android launcher) it sometimes lauch my app to the right and current activty RouteActivity, but after around 1 hour, if I touch the application icon it just restart the application from the beginning and start the LoginActivity.
But if I touch my service notification if bring me back to the right and background activity.
Also my service is not killed, never, so it seems to work just fine, objects and variable tied to the application are still there.
So what ? I have 2 instances of my application running ? I'm kinda lost on this one, especially that it seems to be time related.
This "bug" is produced on Android 2.X, I can't reproduce it on Android 4.X. It is kinda hard to debug because I have to let the application run for around 1 hour. And after that time I have no special message in logcat.
I noted something:
The ActivityManager messages are quite strange, if I launch my application through the service notification in the notification center it log:
Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x14000000 cmp=com.wayzup.wayzupapp/com.wayzup.activity.RouteActivity bnds=[0,149][320,213]
While if I launch it trough the application icon it's logged, but the actual activity shown is not the login one but the actual RouteActivity which actually launched the service. (After around 1 hour it is effectively the LoginActivity which is started).
Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.wayzup.wayzupapp/com.wayzup.activity.LoginActivity bnds=[3,338][77,417]
Each time I launch the RouteActivity I also have this log
Activity idle timeout for HistoryRecord{44e78808 com.wayzup.wayzupapp/com.wayzup.activity.RouteActivity}
This is related to my other question: Android foreground service lose context
But I think this one is the real problem and more accurate.
I can post some code if you want to.
It sounds like you're relying on an activity task stack to maintain all your state. I suggest you use instead use something like PreferenceManager.getDefaultSharedPreferences() to maintain the logged in / logged out state. That way your can survive if its activities are terminated and recreated.
The way I tend to do it is have your main activity be the one you want the user to see once they're logged in. In its onCreate() check to see if the user is logged in and if not, startActivityForResult() to send them off to the login activity. Persist the logged in state somewhere so that you can check it in the main activity's onCreate().
I finally resolved my problem.
Now it does not matter if the activity is killed or not, as long as the service is alive it's fine.
In my LoginActivity I check for if my service, if it's running I start the RouteActivity which is bound to it and singleTop. Si I always have the single and same instance of it.
#Override
public void onResume(){
super.onResume();
if (checkMyServiceRunningOrNot()){
restoreAcitivty();
}
}
public void restoreAcitivty(){
Intent intent = new Intent(this, RouteActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}