Android: make sure that specific activity retains in backstack - android

I want to make so the every activity launches with only one main activity in the backstack, so I always can return to the main activity with back button as for example (for starting activities I use startActivity()):
Main Activity - Activity1 (back pressed) returns to Main
Activity
Main Activity - Activity1 - Activity2 (back pressed) also
returns to Main Activity
It looks like I need to use FLAG_ACTIVITY_CLEAR_TASK flag on launching every new activity, but it clears Main Activity either. I've tried FLAG_ACTIVITY_CLEAR_TOP works fine in the 1st case, but not in the 2nd.
If someone has the same problem, please help. Any thoughts appreciated! Thanks!
My main activity:
<activity
android:name=".activity.main.MainActivity"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTop">
<meta-data
android:name="android.app.default_searchable"
android:value=".activity.search.SearchActivity" />
</activity>

Set FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY flag while firing the intent of Main Activity in onBackPressed().

Just make sure that you call finish() when Activity1 starts Activity2. This will ensure that your task stack looks like this: MainActivity->Activity2.

As #EmmanuelMtali mentioned, I used parent activity for this reason.
For activity different from Main, I set meta-data (and android:parentActivityName, API > 16) as follows:
<activity
android:name=".activity.user.LoginActivity"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTop"
android:parentActivityName=".activity.main.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.main.MainActivity" />
</activity>
When I need to start new activity I use array of intents and startActivities() method:
public static Intent[] createIntentsWithMainActivityInStack(Context context, Intent topMostIntent) {
TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context);
topMostIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
Intent[] intents =
taskStackBuilder
.addNextIntentWithParentStack(topMostIntent)
.getIntents();
return intents; }
and
Intent[] intents = createIntentsWithMainActivityInStack(context, new Intent(context, LoginActivity.class));
startActivities(intents);

Try this in every activity so that when you back press main activity opens:
#Override
public void onBackPressed()
{
super.onBackPressed();
Intent intent = new Intent(ACTIVITYTHREE.this,MAINACTIVITY.class);
startActivity(intent);
}

Related

Single android application with two tasks and its navigation

I am developing a calling application. My HomeActivity is a singleTask activity. My call activity is also a singleTask activity.
From HomeActivity a call is initiated. At this moment, there are two tasks for my application as they both are singleTask. In call screen I have a button to reach my HomeActivity.
When I press the home button in my call screen and navigate back, my call activity is destroyed. But it should not get destroyed. It should remain.
When I press home button in call screen I do the below.
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setClass(this.getActivity(), MyHomeActivity.class);
startActivity(intent);
My manifest declaration:
<activity
android:name=".XXX.MyHomeActivity"
android:label="#string/app_name"
android:alwaysRetainTaskState="true"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustNothing"
android:theme="#style/MyTheme"
>
</activity>
<activity
android:name=".XXX.MyCallActivity"
android:label="#string/app_name"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="#style/MyTheme"
android:windowSoftInputMode="adjustResize">
Inflating HomeActivity from CallActivity:
Intent intent = new Intent();
intent.setClass(this.getActivity(), HomeActivity.class);
startActivity(intent);
Inflating CallActivity:
Intent intent = new Intent();
intent.setClass(this, CallActivity.class);
startActivity(intent);
Can anyone help me with the navigation parameters to use to achieve this?
In order to have 2 tasks, you need to make sure that the root Activity of the tasks have different taskAffinity, otherwise Android will put both in the same task. The default taskAffinity is the name of your package.
Add android:taskAffinity="" to one of the <activity> declarations.
According to the documentation using FLAG_ACTIVITY_CLEAR_TASK will do the following:
this flag will cause any existing task that would be associated with
the activity to be cleared before the activity is started. That is,
the activity becomes the new root of an otherwise empty task, and any
old activities are finished.
So if want your CallActivity to stay in the back stack you can simply do this:
Intent intent = new Intent();
intent.setClass(this.getActivity(), MyHomeActivity.class);
startActivity(intent);

Android android:parentActivityName go to previous Activity

When I press the arrow back I want to navigate to my previous screen(where I came from). So if I'm in MainActivity and go to setting I want the arrow to point back to MainActivity, but if I'm in another Activity I want it to go back to that activity.
<activity
android:name=".SettingsActivity"
android:label="#string/action_settings"
android:parentActivityName=".MainActivity" >
</activity>
How do you start settings activity?
Just use startActivity(intent) in main activity and don't call finish() method. It should be handled automatically.
sample code:
Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
startActivity(intent);
just comment what's your problem exactly and I'll edit my answer.

Could an activity with single task flag be cleared via clear top?

Assume that I have a launcher activity A which has singleTask launch mode. Now imagine that A starts Activity B like;
Intent intent = new Intent(this, B.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivityForResult(intent, REQ_ACCOUNT_ACTIVITY);
In this case it seems like A's not destroyed. I wonder if it's correct or did I something wrong?
You can achieve this by using below attribute in AndroidMenifest file
android:finishOnTaskLaunch="true"
<application
...
>
<activity
android:finishOnTaskLaunch="true"
android:launchMode="singleTask"
...>
</activity>
</application>
If you not want to kept new activity in the history stack. Use below one_
FLAG_ACTIVITY_NO_HISTORY
//Actvity B is not in BackStack if we set FLAG_ACTIVITY_NO_HISTORY flag
Intent intent = new Intent(this, B.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivityForResult(intent, REQ_ACCOUNT_ACTIVITY);
As soon as the user navigates away from Activity B, the activity is finished. This may also be set with the noHistory attribute.
For more -> Tasks and Back Stack
It's been a while but I came across my own question when I was wandering over here.
The activity A was not getting deleted because the flags are set for the activity B. And when I call this code, there's no such activity called B in the task stack already. So effectively, nothing to clear on top of B.
Hope this helps.

Activity is getting created again instead of resuming and using instance from stack?

I have an android application with several activities .Each and every activity has Application Icon in action bar which helps user to return back to main activity directly instead of pressing back button.My problem is that when I use the icon to start my home activity it does not uses the previous instance from the stack and start creating it again.
My Action bar app icon code is :
startActivity(new Intent(this, DashBoard.class)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
this above code starts Dashboard activity and calls its both onCreate() and onResume().But If I uses back button to return to this activity from any activity it just calls onResume().
Activity definition from manifest file:
<activity
android:name=".DashBoard"
android:configChanges="keyboardHidden"
android:label="#string/app_name"
android:screenOrientation="portrait" >
</activity>
Why is this happening?Am I missing something to prevent it from not creating it again?Please help
Thanks
Use setFlags(), instead of addFlags(). You are on right track. Use the following code.
Intent intent = new Intent(this, DashBoard.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
Remove the FLAG_ACTIVITY_CLEAR_TOP.

Android: bug in launchMode="singleTask"? -> activity stack not preserved

My main activity A has as set android:launchMode="singleTask" in the manifest. Now, whenever I start another activity from there, e.g. B and press the HOME BUTTON on the phone to return to the home screen and then again go back to my app, either via pressing the app's button or pressing the HOME BUTTONlong to show my most recent apps it doesn't preserve my activity stack and returns straight to A instead of the expected activity B.
Here the two behaviors:
Expected: A > B > HOME > B
Actual: A > B > HOME > A (bad!)
Is there a setting I'm missing or is this a bug? If the latter, is there a workaround for this until the bug is fixed?
FYI: This question has already been discussed here. However, it doesn't seem that there is any real solution to this, yet.
This is not a bug. When an existing singleTask activity is launched, all other activities above it in the stack will be destroyed.
When you press HOME and launch the activity again, ActivityManger calls an intent
{act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]flag=FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_RESET_IF_NEEDED cmp=A}
So the result is A > B > HOME > A.
It's different when A's launchMode is "Standard". The task which contains A will come to the foreground and keep the state the same as before.
You can create a "Standard" activity eg. C as the launcher and startActivity(A) in the onCreate method of C
OR
Just remove the launchMode="singleTask" and set FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP flag whenever call an intent to A
From http://developer.android.com/guide/topics/manifest/activity-element.html on singleTask
The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one.
This means when the action.MAIN and category.LAUNCHER flags targets your application from the Launcher, the system would rather route the intent to the existing ActivityA as opposed to creating a new task and setting a new ActivityA as the root. It would rather tear down all activities above existing task ActivityA lives in, and invoke it's onNewIntent().
If you want to capture both the behavior of singleTop and singleTask, create a separate "delegate" activity named SingleTaskActivity with the singleTask launchMode which simply invokes the singleTop activity in its onCreate() and then finishes itself. The singleTop activity would still have the MAIN/LAUNCHER intent-filters to continue acting as the application's main Launcher activity, but when other activities desire calling this singleTop activity it must instead invoke the SingleTaskActivity as to preserve the singleTask behavior. The intent being passed to the singleTask activity should also be carried over to the singleTop Activity, so something like the following has worked for me since I wanted to have both singleTask and singleTop launch modes.
<activity android:name=".activities.SingleTaskActivity"
android:launchMode="singleTask"
android:noHistory="true"/>
public class SingleTaskActivity extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
intent.setClass(this, SingleTop.class);
startActivity(intent);
}
}
And your singleTop activity would continue having its singleTop launch mode.
<activity
android:name=".activities.SingleTopActivity"
android:launchMode="singleTop"
android:noHistory="true"/>
Good luck.
Stefan, you ever find an answer to this? I put together a testcase for this and am seeing the same (perplexing) behavior...I'll paste the code below in case anyone comes along and sees something obvious:
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example" >
<uses-sdk android:minSdkVersion="3"/>
<application android:icon="#drawable/icon" android:label="testSingleTask">
<activity android:name=".ActivityA"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ActivityB"/>
</application>
</manifest>
ActivityA.java:
public class ActivityA extends Activity implements View.OnClickListener
{
#Override
public void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.main );
View button = findViewById( R.id.tacos );
button.setOnClickListener( this );
}
public void onClick( View view )
{
//Intent i = new Intent( this, ActivityB.class );
Intent i = new Intent();
i.setComponent( new ComponentName( this, ActivityB.class ) );
startActivity( i );
}
}
ActivityB.java:
public class ActivityB extends Activity
{
#Override
public void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.layout_b );
}
}
I tried changing minSdkVersion to no avail. This just seems to be a bug, at least according to the documentation, which states the following:
As noted above, there's never more than one instance of a "singleTask" or "singleInstance" activity, so that instance is expected to handle all new intents. A "singleInstance" activity is always at the top of the stack (since it is the only activity in the task), so it is always in position to handle the intent. However, a "singleTask" activity may or may not have other activities above it in the stack. If it does, it is not in position to handle the intent, and the intent is dropped. (Even though the intent is dropped, its arrival would have caused the task to come to the foreground, where it would remain.)
I think this is the behaviour you want:
singleTask resets the stack on home press for some retarded reason that I don't understand.
The solution is instead to not use singleTask and use standard or singleTop for launcher activity instead (I've only tried with singleTop to date though).
Because apps have an affinity for each other, launching an activity like this:
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
if(launchIntent!=null) {
launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}
will cause your activty stack to reappear as it was, without it starting a new activity upon the old one (which was my main problem before). The flags are the important ones:
FLAG_ACTIVITY_NEW_TASK Added in API level 1
If set, this activity will become the start of a new task on this
history stack. A task (from the activity that started it to the next
task activity) defines an atomic group of activities that the user can
move to. Tasks can be moved to the foreground and background; all of
the activities inside of a particular task always remain in the same
order. See Tasks and Back Stack for more information about tasks.
This flag is generally used by activities that want to present a
"launcher" style behavior: they give the user a list of separate
things that can be done, which otherwise run completely independently
of the activity launching them.
When using this flag, if a task is already running for the activity
you are now starting, then a new activity will not be started;
instead, the current task will simply be brought to the front of the
screen with the state it was last in. See FLAG_ACTIVITY_MULTIPLE_TASK
for a flag to disable this behavior.
This flag can not be used when the caller is requesting a result from
the activity being launched.
And:
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED Added in API level 1
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. This will result in the application of any
affinities needed to have that task in the proper state (either moving
activities to or from it), or simply resetting that task to its
initial state if needed.
Without them the launched activity will just be pushed ontop of the old stack or some other undesirable behaviour (in this case of course)
I believe the problem with not receiving the latest Intent can be solved like this (out of my head):
#Override
public void onActivityReenter (int resultCode, Intent data) {
onNewIntent(data);
}
Try it out!
I've found this issue happens only if the launcher activity's launch mode is set to singleTask or singleInstance.
So, I've created a new launcher activity whose launch mode is standard or singleTop. And made this launcher activity to call my old main activity whose launch mode is single task.
LauncherActivity (standard/no history) -> MainActivity (singleTask).
Set splash screen to launcher activity. And killed launcher activity right after I call the main activity.
public LauncherActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
startActivity(intent);
finish();
}
}
<activity
android:name=".LauncherActivity"
android:noHistory="true"
android:theme="#style/Theme.LauncherScreen">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Launcher screen theme should be set for the case that app is restarting after the process is killed. -->
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:theme="#style/Theme.LauncherScreen"/>
Pros: Can keep the MainActivity's launch mode as singleTask to make sure there always is no more than one MainActivity.
If both A and B belong to the same Application, try removing
android:launchMode="singleTask"
from your Activities and test because I think the default behavior is what you described as expected.
Whenever you press the home button to go back to your home screen the activity stack kills some of the previously launched and running apps.
To verify this fact try to launch an app from the notification panel after going from A to B in your app and come back using the back button ..........you will find your app in the same state as you left it.
When using launch mode as singleTop make sure to call finish() (on current activity say A) when starting the next activity (using startActivity(Intent) method say B). This way the current activity gets destroyed.
A -> B -> Pause the app and click on launcher Icon, Starts A
In oncreate method of A, you need to have a check,
if(!TaskRoot()) {
finish();
return;
}
This way when launching app we are checking for root task and previously root task is B but not A. So this check destroys the activity A and takes us to activity B which is currently top of the stack.
Hope it works for you!.
This is how I finally solved this weird behavior. In AndroidManifest, this is what I added:
Application & Root activity
android:launchMode="singleTop"
android:alwaysRetainTaskState="true"
android:taskAffinity="<name of package>"
Child Activity
android:parentActivityName=".<name of parent activity>"
android:taskAffinity="<name of package>"
Add below in android manifest activity, it will add new task to top of the view destroying earlier tasks.
android:launchMode="singleTop" as below
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:launchMode="singleTop"
android:theme="#style/AppTheme.NoActionBar">
</activity>
In child activity or in B activity
#Override
public void onBackPressed() {
Intent intent = new Intent(getApplicationContext(), Parent.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
finish();
}
<activity android:name=".MainActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
//Try to use launchMode="singleTop" in your main activity to maintain single instance of your application. Go to manifest and change.

Categories

Resources