popping a known number of activities from stack - android

in the process of building an app, I have an Activity lets call it A, and i open in multiple times, as A(1) -> A(2) -> A(3) -> ... -> A(m) -> ... -> A(n).
In the app I want to pop all the way back to A(m), is there a way to pop activities all the way back to a chosen one?
i already saw a few answers that had the FLAG_ACTIVITY_CLEAR_TOP but it seems to me like that is only to clear the whole stack and go to the first element in the stack and that is not what I'm trying to get.
update:
what I'm trying to achieve here is as follows: I have a list of questions, and the user can choose a different answer for each one.
once he chooses, the question and the answer go into a list of history, and another question is prompted. at any given point, the user can click a history button and go back to the question he chooses.
all the Q&A activities are the same, so going back to a question means popping the stack until getting there.

If I understand you correctly you are saying that you have N number of the SAME activity and wish to go back to some arbitrary starting point activity in the stack?
I am not totally clear on whether you know ahead of time that all activities forward of your "sticky" activity should be finished or if this is determined somewhere along the way
I don't know of any arbitrary way of popping N activities off the stack when they are duplicate activities but you could roll your own, such as (not finished code)
If you know ahead of time that A(M) will be sticky before it launches the next activity(s) you could just tell each forward activity that it needs to kill itself once it launches it's new task by passing it a flag then when the last activity in the chain ends you are back to A(M)
in A(...)
startSelf()
{
Intent I = new Intent(this,MyActivity.class);
if (bFinishSelf || bFinishForward)
I.putExtra("finishself", 1);
if (Finishelf)
finish();
startActivity(I);
in ... all A(...) onCreate( ...
Bundle b = getIntent().getExtras();
if(b !=null && extras.getInt("finishself");
bFinishSelf = true;
Then when you start a new activity check if the finishself flag is set and if so call finish() after starting new activity ...
If on the other hand A(z) is the one that determines you need to return to A(...) then I guess in A(z) you could tell the calling parent(s) that they need to kill themselves until reaching some ID contained in data ...
A(M)
startActivityForResult(...)
A(Z) somehow you determine that A(M) should be the sticky activity ...
finshAndStopAt(int ID) {
Intent I = new Intent();
I.putExtra("finish",ID);
if (getParent() == null) {
setResult(Activity.RESULT_OK, I);
} else {
getParent().setResult(Activity.RESULT_OK, I);
}
finish();
}
All A(z ... m) (parent's) would monitor for the return data
#Override void onActivityResult(int requestCode, int resultCode, Intent data) {
// If we are asked to finish ourselves, pass it on ...
if (null != data && data.getInt("finish",stopAt) != myID) {
if (getParent() == null) {
setResult(Activity.RESULT_OK, data);
} else {
getParent().setResult(Activity.RESULT_OK, data);
finish();
}
// else we can hang around and are the sticky activity
}
It's a bit of pain and maybe someone knows of an easier method

As a generic response, I would say to keep popping until the Activity returned by the pop is the one you want, then maybe push it back (if you need to).

Have you considered using Fragments? In this way you can use an instance of the FragmentManager class to handle the fragments history that you have generated through methods of the class FragmentTransaction. You can also tag your transactions and then pop all back stack states up to a transaction with a given name.
I don't know if there is a similar way to do this with Activities.
Take a look here:
http://developer.android.com/reference/android/support/v4/app/FragmentManager.html
http://developer.android.com/reference/android/support/v4/app/FragmentTransaction.html
If you use the support library you don't have to worry about you target SDK.

Related

How to kill a specific Instance of a Activity?

I know we can keep only one instance by
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
but I need that my app can create maximum two instances of certain activity not more than that.
I am using a chained search feature like image below
When Instance3 of the activity1 is created i want to destroy:-
Instance1 of activity1
Instance1 of activity2
Tried to used this to kill a specific activity but activity have same Pid for all processes in an app
android.os.Process.killProcess( stack.getLast());
is there a way we can moderate which instances should be kept alive?
any help would be great thanks!
In my opinion this is the wrong architecture. For chained search you should only ever have a single instance of each Activity. You should flip between the different Activity instances by calling startActivity() and setting Intent.FLAG_ACTIVITY_REORDER_TO_FRONT in the Intent you use. Also add the data you want to display as "extras" in the Intent.
To be able to use the BACK button to back through the chain (no matter how long it is), each Activity should manage a stack that contains the data that it needs to recreate the page whenever the user backs into it. In onCreate() and in onNewIntent() the data (from the "extras") should be pushed onto the stack and displayed. You then override onBackPressed() and go back to the previous Activity by calling startActivity(), and setting Intent.FLAG_ACTIVITY_REORDER_TO_FRONT in the Intent you use. You also add an "extra" to the Intent that indicates the user wants to "go back". In onBackPressed() you should also discard the top element off the data stack of the Activity that is being left. This will ensure that the stack is correct when the user then backs into this Activity.
In onNewIntent() if the user just backed into the Activity, you just display the data that is already on top of the stack of managed data.
In this way, you only ever have one instance of each Activity, the user can chain all day through the set of activities and the BACK button always works and you don't have to worry about running out of memory.
Trying to accomplish this using taskAffinity or Intent flags or similar will not work. Don't waste your time. It is also bad programming style.
I hope this is clear.
Basically you want to remove entries 1 and 2 from the backstack when you create the 5th one, but leave the 3rd and 4th. Unfortunately, the backstack doesn't work that way, you can only manipulate it from the top. You have the option of clearing all the activities except the last one if you set the flags FLAG_ACTIVITY_CLEAR_TASK and FLAG_ACTIVITY_NEW_TASK, you will have an empty task only with the just started activity.
If I were you, I wouldn't worry about memory consumption by the old activities though. As long as you stop the resource-consuming processes in your activities when they leave the screen and don't hold the references to them so they can be garbage collected, Android can manage the memory itself fine. What you should think about is whether it makes sense for the user to come back to the old activities if he presses back. If yes, then leave them, if no, then don't.
In case you want to kill all your activities on the back button press, there is an Activity.finishAffinity() method. Just override the onBackPressed method to call it.
You can use a little hack with EventBus or BroadcastReceiver or some other Bus. Check my following code for my idea. For this app, I will keep maximum 2 instances of MainActivity
public class MainActivity extends AppCompatActivity {
public static int count = 0;
int id;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
count ++;
id = getIntent().getIntExtra("ID", 0);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, MainActivity.class);
intent.putExtra("ID", count);
startActivity(intent);
}
});
}
#Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
EventBus.getDefault().post(new Event(count));
}
#Subscribe
public void onEvent(Event event) {
if (id < (event.getID() - 2)) {//put your logic code to finish the activity here
Log.i("MainActivity", id + " is killed ");
finish();
}
}
#Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
}
you can do this bro,
declare one Activity object like this
public static Activity factivity;
onCreate()
{
factivity = this;
}
now you can use that object to kill that specific activity on another activity like this:
onCreate()
{
FirstActivity.factivity.finish();
}

How to Quit Android App

I have an Android App, and has several activities. I need to provide the button for each activity or specific activity which should allow to Close the App, without going to back to previous activities or run in background.
I tried
finish();
System.exit(0);
Both combination and individually its not working but closing the current activity and navigate to previous activity.
I looked the code from the following question
Need code for exit from app in android
First, having a Quit button is not reccomended in Android as it's not part of the expected UX.
But if you really need it, then you can call your home activity with an intent containing an extra, for instance :
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.putExtra("FORCE_EXIT", true);
Finally in your home activity, when handling the intent (in the onNewIntent() method), if the "ForceExit" extra is set, then finish the activity.
The stack will have been cleared completely by the FLAG_ACTIVITY_CLEAR_TOP and your app will then stop.
The most recommended approach that works for most cases is to feature only 1 Activity, using fragments for content displaying and logic.
This way you only need to finish() the main Activity since it will control the app lifecycle by design.
You will have many other benefits, such as dependency control and reusability, aswell as built-in functionality like animations using fragment transactions while having the possibility of keeping a fragment backstack, which you can manage accordingly towards your expected user interaction and without affecting the conveniency of finishing your app by calling finish() on your host Activity.
Another thing you can do, is to flag intents like this: intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); before launching a new Activity.
This way you can maintain your back trace clean, hence finishing the application whenever the user press the back button or call finish() from any event. However the use of flags is discouraged and considered bad practise.
This may be a hack to solve your problem. but i have just made an app and tested my code and it is working fine.
You will need to create a new activity called QuitActivity or whatever you want to name it and when you want to finish your app or quit your app you will have to start that activity via using this code
Intent i = new Intent(getApplicationContext(), QuitActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
this.finish();
then this is my code for quit activity that does nothing but closes it self after clearing the backstack so your app will quit.
public class QuitActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
this.finish();
}
}
hope this helps you.
First Finish your current Activity while moving to next by this code
startActivity(intent);
Classname.this.finish();
Second thing just override onBackPressed
#Override
public void onBackPressed() {
//do nothing
}
Use:
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
when starting a new Activity, as described in API Documentation.
type only System.exit(0);
or create static boolean needToExit;
set needToExit = true;
in place where you call
finish();
System.exit(0);
in all other activity overwrite onresume
public void onResume(){
super.onResume();
if (MySettingsClass.needToExit){
finish();
System.exit(0);
return;
}
//your other code;
}
I tried some of the answers here using the flags or System.exit(0), but for me it either didn't work or it resulted in weird behavior. Sometimes it would kill the app, but then immediately restart it. I realized that I should just be doing things more of the standard way using request and result codes.
Basically, in your parent activity, start your child activity with:
startActivityForResult(new Intent(this, ChildActivity.class), CHILD_ACTIVITY_REQUEST_CODE);
where CHILD_ACTIVITY_REQUEST_CODE is just a constant (static final) integer. Then in your child activity, when they press the exit button, finish the activity with:
setResultAndFinish(RESULT_CODE_EXIT);
where RESULT_CODE_EXIT is another constant (static final) integer. Then back in your parent activity, handle the result code and finish the parent activity too:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == CHILD_ACTIVITY_REQUEST_CODE) {
if(resultCode != ChildActivity.RESULT_CODE_EXIT) {
finish();
return;
}
}
}

Android - how to start chain of activities?

I intend to start 3 activities in a chain (like from main open Activities A, B and then C, which will be visible for the user), but I wasn't able to find some way how to do that in Android. Do not ask me why, I just have to do that for restoring my application state, where is was before.
Thanks for any ideas
Waypoint
Edit:
Ok, I have tried opening activities in For cycle, but they aren't opened properly. They are chained, but recreated only when I press back button and they display to me. I need some solution which leads to: open A, if A is opened check if needs to open B -> YES, open B, check if needs to open C -> YES, open C, no need to open another activity -> FINISH
prior to start any activity , decide which activity should be start .
Lets take your case >>first check for B , if yes check for C , now open the required 1 .
i understand comparable data is inside the activities, but a right data structure will always allow you to access the data wherever and whenever it actually requires .
For future readers: if you want to start activity with proper back stack, you should use TaskStackBuilder.
define the natural hierarchy for your activities by adding the android:parentActivityName attribute to each element in your app manifest file
create an instance of TaskStackBuilder and call addNextIntentWithParentStack(), passing it the Intent for the activity you want to start.
For more details see official documentation
Override the onResume-method in each activity. Add the check and the start of the activity there.
public void onResume() {
if( condition )
startActivity( intentForTheNextActivity );
}
Where condition is whatever condition you might have (in your example if B should be started, C should be started etc.) and intentForTheNextActivity is the intent for the following activity in the chain (e.g. if now in A, the intent is for B etc.).
I'm having a very hard time understanding exactly what it is you're trying to do. Sometimes it seems like it's a chain (A opens B, B opens C and so forth) sometimes it seems you want some random flow (A opens B, B opens A, A opens B, B opens C) - which makes it really hard to give you a specific answer.
What I can do, is recommend that you read up on the following:
Activity Lifecycle
Starting Activities and getting results (in particular the methods startActivityForResult and setResult)
If you need more help than this - you need to explain yourself better (maybe with a diagram or some sample code of what you have tried so far).
You didn't provide any information of what you've tried, so i'll give you the simplest answer:
method startActivity(Intent intent), more info here.
Edit: Hmm, how about this? I don't have SDK around me now, but i can provide a concept. I'm not sure if it works, but i hope it'll guide you to soultion.
Let's imagine this is ActivityA's code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (doINeedActivityB) {
Intent activityC = new Intent(this, ActivityC.class);
this.startActivity(activityA);
}
if (doINeedActivityC) {
Intent activityC = new Intent(this, ActivityC.class);
this.startActivity(activityC);
}
}

Android finishActivity always causing onActivityResult to get RESULT_CANCELED

I have a main Activity A that can kick off sub-Activities B, C, or D. I start them using startActivityForResult(newIntent, REQUEST_CODE);
The user can choose to navigate between B, C, and D once one is displayed. Activity A controls that navigation. When they choose swap between screens 'A' first calls finishActivity(REQUEST_CODE); on the one displayed and then call startActivityForResult(newIntent, REQUEST_CODE); for the next one.
In my onActivityResult I've got
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
// these logs are just for my debugging
Log.w(this.toString(), "onActivityResult returned to by " + requestCode);
Log.w(this.toString(), "result code = " + resultCode );
// the Activity set this value if it was returning normally
if (RESULT_OK == resultCode)
{
Log.i(this.toString(), "---- the sub-activity returned OK ----");
// do some stuff
}// end if (RESULT_OK == resultCode)
// Else we got here because the user hit the back button or something went
// went wrong (RESULT_CANCELED). Either way we do not want to display this
// blank screen to the user. Take them back to the device connection screen.
else if (RESULT_CANCELED == resultCode)
{
finish();
}
}
For some reason I'm getting back a zero or RESULT_CANCELED everytime even though in my sub-activities B,C,D I only set that result if the user chooses to hit the back button (I display an "are you sure" dialog and they choose to exit. In B,C,D I have the following in onPause. The userExiting is a flag I set from the Exit dialog.
protected void onPause()
{
Log.i(this.toString(), "onPause");
// hack to try to setResult for an activity whose finishActivty was called
if ( !this.exiting )
{
Log.i(this.toString(), "======== RESULT_OK ========");
Intent returnIntent = new Intent();
setResult(RESULT_OK, returnIntent);
finish();
}
displayed = false;
super.onPause();
}
Any ideas?
Edit - to be clear the issue is that after calling finishActivty on the old Activity, since a result was never set, Activity A always thinks it should exit. One weird side items. This doesn't happen until the user navigates two times. In other words B is displayed and they choose to go to C. Works, but I can see in the log that onActivityResult hasn't been called. User selects to go from C to D, boom, I see two calls to onActivtyResult, 'A; exits and D is left displayed and the user is unable to nav away.
Update: Since this has been getting a lot of views I thought I'd post this update. The project evolved such that sometimes a single Activity is displays and sometimes a group are displayed as tabs. When the user packs out of the tab in some cases data needed to be returned to the Activity that kicked off the tabs. Before I added the code below RESULT_CANCELED would always be returned. Also make sure that setResult is called before finish.
if (getParent() == null)
{
setResult(Activity.RESULT_OK, intent);
}
else
{
getParent().setResult(Activity.RESULT_OK, intent);
}
I'm not quite following everything you are doing here, but it doesn't sound like you are navigating between activities properly. I would highly suggest reading through the notepad example thoroughly so that you understand the android way of moving between Activities.
Here is what I understand regarding what you are trying to accomplish: Activity A can navigate to B, C or D. From B, C or D you can navigate to any of the others.
You are somehow trying to using Activity A as a controller, having it finish other Activities. Don't do this. Each Activity should be responsible for finishing itself at the proper point.
If you want to go from having A -> B on the stack to A -> C, then I would do this:
A starts B for result. When user selects something in B asking to go to C you have a few choices. You could have B start C not for result, then finish. This would have the effect of B being removed from the stack when C exits. Or, you could have B finish itself, passing back a note that C should be loaded in the results intent. A could then load C for result.
I'm really not sure why you want this behavior though. As an Android user, if I go from A -> B -> C, I expect hitting back to take me back to B.
As an aside, your "is exiting" hack doesn't make any sense. By the time you get to onPause, the result has been set. You cannot change it here. You need to properly set the result when you are finishing, before onPause.

startActivityForResult not working properly with launchMode singleInstance

I'd like Activities on my application's Activity stack to only have one instance. I have several screens which are ListActivities and I'd like to not go through the pain and suffering of updating the lists in a previous instance of the ListActivity when another instance of that ListActivity is changed (added to, edited, removed from, etc) (or is there an easy way to do this?).
Note: I've read that singleTop will accomplish this (though it destroys the Activity if you hit the back button), but it does not work. I have a menu and if I go to my Inbox screen, then I go to my QuickList screen, and then I go to my Inbox screen again, it creates a new Inbox Activity.
Right now, on my ListActivities, I have launchMode set to singleInstance. The problem is: If I launch another Activity using startActivityForResult, the onActivityResult handler fires right away (before the new Activity is created). When I perform the necessary action on the next screen to return the result, the onActivityResult handler does not fire.
What is going on?
Here is how I fire the new Activity:
Intent intentLaunchQuickList = new Intent(ActivityMyList.this, ActivityQuickList.class);
startActivityForResult(intentLaunchQuickList, REQUEST_QUICKLIST);
Here is how I return the result:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
QuickListItem qlItem = m_Adapter.getItem(position);
if (qlItem != null && qlItem.getQLId() != -1) {
Intent data = new Intent();
data.putExtra("ql_id", qlItem.getQLId());
if (getParent() == null) {
setResult(Activity.RESULT_OK, data);
}
else {
getParent().setResult(Activity.RESULT_OK, data);
}
}
finish();
}
Here is my onActivityResult handler:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_QUICKLIST) {
if (resultCode == Activity.RESULT_OK) {
Bundle extras = data.getExtras();
if (extras != null) {
int id = extras.getInt("ql_id");
if (id > 0) {
launchQLItemsThread(id);
}
}
}
}
}
From the documentation of startActivityForResult: "For example, if the activity you are launching uses the singleTask launch mode, it will not run in your task and thus you will immediately receive a cancel result." singleInstance activities are the same way.
In other words, if you want to use sAFR, you will need to handle multiple activity instances. What I would advise is storing the list state for your ListActivity instances in onPause to some app-global spot (a singleton or whatever), and loading from there in onResume. Then, even if multiple ListActivity instances will get created, the top one will always update the data before the older ones get resumed, and the lists will always appear current to the user.
Note that you should be doing that anyway if your data is meant to be persistent, because your whole process can be killed by the system any time after an onPause call, and if you haven't saved any changes somewhere by the time that returns, they are liable to get silently lost under some -- often rare and unpredictable -- circumstances. In this case you want to be using local files or SQLite databases, not persisting to the network. onPause needs to return quickly because the user can't interact with the system while it's running, so save to local storage and then sync to the network at some other time, perhaps via a service launched by onPause.
I have several screens which are
ListActivities and I'd like to not go
through the pain and suffering of
updating the lists in a previous
instance of the ListActivity when
another instance of that ListActivity
is changed (or is there an easy way to
do this?).
Use a consistent model. For example, your data is hopefully in a database. Each ListActivity has a Cursor on the portion of the database it needs. Have that Cursor be a "managed Cursor" (via startManagingCursor()), and your ListViews will update automatically in onResume(). You then make your changes to your model via the database.
I have a menu and if I go to my Inbox
screen, then I go to my QuickList
screen, and then I go to my Inbox
screen again, it creates a new Inbox
Activity.
That's what it is supposed to do. Quoting the documentation:
The "standard" and "singleTop" modes
differ from each other in just one
respect: Every time there's new intent
for a "standard" activity, a new
instance of the class is created to
respond to that intent. Each instance
handles a single intent. Similarly, a
new instance of a "singleTop" activity
may also be created to handle a new
intent. However, if the target task
already has an existing instance of
the activity at the top of its stack,
that instance will receive the new
intent (in an onNewIntent() call); a
new instance is not created. In
other circumstances — for example, if
an existing instance of the
"singleTop" activity is in the target
task, but not at the top of the stack,
or if it's at the top of a stack, but
not in the target task — a new
instance would be created and pushed
on the stack.
(boldface added for emphasis)
Right now, on my ListActivities, I have launchMode set to singleInstance.
Please do not do this.

Categories

Resources