可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a task stack of A > B > C. I am currently on C, and then I press the home button. I get a notification with the intent to take me to Activity A. I press the notification, and I'm at A but if I press back, I go to C, then B, then A.
I am setting up my PendingIntent like so. Anything clearly wrong with it?
final Intent notificationIntent = new Intent(mContext, ActivityA.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(myContext, 0, notificationIntent, 0);
EDIT 1:
I tried the suggestion here: Clear all activities in a task?
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
but I still get the same result. My activity A starts in my application task stack, I press back and go to C, then B, then A again.
I am starting to think that this is either not possible in Android or it's not possible when using a pending intent.
EDIT 2:
This is not a matter of what flags are needed. More of an issue of what could be going wrong that the flags seem to have no effect.
回答1:
if u r using solution 1 then Just remove the launchMode="singleTask"
from manifest
1)
final Intent notificationIntent = new Intent(mContext, ActivityA.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(myContext, 0, notificationIntent, 0);
OR
2)
<activity
android:name=".ActivityA"
android:theme="@style/theme_noActionBar"
android:noHistory="true">
OR
3)
<activity android:name=".ActivityA" android:launchMode="singleTop">
回答2:
The problem you are encountering has to do with Android creating a new task using your PendingIntent
and the required Intent.FLAG_ACTIVITY_NEW_TASK
. In other words, the flags you are wanting to use have to do with starting activities within the context of a given task stack. A PendingIntent
usually does not have a context, and therefore what you are seeing is a task being created for your Activity A pending intent.
Here's the reference to the docs:
Android PendingIntent
The requirement for the first in a series of PendingIntent
s to have Intent.FLAG_ACTIVITY_NEW_TASK
is the issue. Note that a series of pending intents can have the same context as the initial pending intent using the "getActivities" method.
You are correct that it is not possible to do what you want only using flags. Your code is correct, and to get the behavior that you want you will need too look beyond intent flags.
There are many other ways, such as using the manifest, running a service, using static fields, etc. to try to manage what you are interested in doing. Besides that, you could possibly use TaskStackBuilder
to solve your problem if you decide to go the route of using the Manifest. There are also many other solutions not involving flags, but you specifically asked about using flags.
回答3:
You can set the launchMode
of ActivityA to singleTask
in Manifest file. Since ActivityA
is the root of your application it works.
From documentation:
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.
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTask" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
EDIT 1:
Since you don't want to modify Manifest file, only option I can see is using Intent.makeRestartActivityTask method to generate the Intent. However this will relaunch the ActivityA rather than resuming existing instance.
Intent intent = Intent.makeRestartActivityTask(new ComponentName(this, ActivityA.class));
PendingIntent rIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
回答4:
A solution in terms of intent flag would be as following:
- In Manifest, add
launchMode:singleTask
for Activity A
Use the following code block:
final Intent notificationIntent = new Intent(mContext, ActivityA.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(myContext, 0, notificationIntent, 0);
What happens here is that when Activity A is launched using notification, by the definition of Intent.FLAG_ACTIVITY_NEW_TASK
, since there exists a task A>B>C containing A, this task is brought forward also destroying the activities B and C and A starts with the onNewIntent --> onRestart --> onStart --> onResume
If a task is already running for the activity you are now starting, that task is brought to the foreground with its last state restored and the activity receives the new intent in onNewIntent()
Now the reason, why it doesn't behave nicely with just the intent flags is that, the Activity A wasn't launched with singleTask
mode and Android somehow doesn't maintain the state of it.
回答5:
I had loads of problems with this, and thought I was going crazy until I uninstalled/reinstalled my App and hey presto, the code works as per many posts on Stack Overflow. I was doing the following:
Intent i = new Intent(getApplicationContext(), MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0);
and was continually getting two versions of the App. After an uninstall on one device, and re-starting the other device everything now behaves as expected with a single instance. Hope this helps you.
回答6:
Try using the Intent flag FLAG_ACTIVITY_CLEAR_TOP
instead of clearing the task. From the Intent documentation:
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.
For example, consider a task consisting of the activities: A, B, C, D.
If D calls startActivity() with an Intent that resolves to the
component of activity B, then C and D will be finished and B receive
the given Intent, resulting in the stack now being: A, B.
回答7:
My solution code:
public class MyApplication extends Application{
private static int activityCounter;
public Activity A,B,C;
public void incrementActivityCounter()
{
activityCounter++;
}
public void decrementActivityCounter()
{
activityCounter--;
if (activityCounter == 0)
{
A.finish();
B.finish();
C.finish();
}
}
}
public class A extends Activity{
@Override
protected void onStart()
{
super.onStart();
application.incrementActivityCounter();
}
@Override
protected void onStop()
{
application.decrementActivityCounter();
super.onStop();
}
}
回答8:
I created a demo project as your description and it works just as what you want.
I don't know how do you get the notification, so in my project I use a BroadcastReceiver to get a Broadcast and then show the Notification in the onReceive() method of the Receiver. I think you can compare my demo with your project to find the problem. BTW, my test is performed under API 16.
Here is my demo:
https://github.com/leoguo123/AndroidDemo
In the repository:
Test project contains 3 Activity(A > B > C) and it can show the Notification when receive a proper broadcast.
SendBroadcast project is responsible for sending the broadcast.
The code for generating the notification:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
showNotification(context);
}
private void showNotification(Context context) {
Intent intent = new Intent(context, ActivityA.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_NEW_TASK);
NotificationManager nmr = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 0);
Notification.Builder builder = new Notification.Builder(context);
builder.setContentTitle("Open A with B and C being closed");
builder.setContentText("Open A with B and C being closed");
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentIntent(pi);
builder.setAutoCancel(true);
Notification nt = builder.build();
nmr.notify(0, nt);
}
}
回答9:
Tried all suggestions from above and failed and the came up with a solution from the guideline from https://stackoverflow.com/a/24999724/4271334 and https://stackoverflow.com/a/26592562/4271334.
Added following 2 lines in the manifest
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
and set the PendingIntent at NotificationCompat.Builder using
mBuilder.setContentIntent(addBackStack(mContext,intent));
where
public static PendingIntent addBackStack(final Context context, final Intent intent) {
TaskStackBuilder stackBuilder = TaskStackBuilder.create (context.getApplicationContext ());
stackBuilder.addNextIntentWithParentStack (intent);
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
return stackBuilder.getPendingIntent (0,PendingIntent.FLAG_UPDATE_CURRENT);
}
Hope someone will be helped.