恢复应用程序及其堆栈的通知

73
我希望能够从状态栏通知中恢复我的应用程序,方式与用户在启动器中轻触其图标时完全相同。
也就是说: 我希望堆栈处于用户离开之前的相同状态。
设置挂起意图时的问题是它总是针对特定活动。但我不想要这个。我需要像启动器一样恢复应用程序。
因此,如果用户在A活动中,则我希望恢复A活动。如果他从A活动启动了B活动,则我希望在用户点击通知时显示B活动,并恢复堆栈,以便在B活动中点击返回按钮时恢复A活动。
还有几个其他类似标题的问题,但没有一个解决我的问题。

可能的重复问题:https://dev59.com/ynA75IYBdhLWcg3wVXWv, https://dev59.com/sW855IYBdhLWcg3w_5cQ - Philipp
这个回答解决了您的问题吗?如何通过通知将Android现有的Activity带到前台 - Delwyn Pinto
8个回答

171

只需使用与Android在启动应用程序时使用的相同意图过滤器即可:

final Intent notificationIntent = new Intent(context, YourActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

由于你从通知栏打开Activity所创建的Intent与Android用于启动应用程序的相同,因此会显示先前已打开的Activity而不是创建新的Activity


3
一个问题:如果某个特定/替代启动器使用略有不同的意图,可能还包括其他附加信息或类别,你觉得我应该缓存我的应用程序启动时使用的原始意图(通过Activity.getIntent()检索),并将其原封不动地用于创建待定意图吗? - olivierg
2
我已经解决了这个问题,我需要激活启动画面和主活动页面。在使用上述代码修改我的代码后,它可以恢复到主活动页面而不是重新启动新的活动页面。非常感谢您的帮助。 - Sai
7
对我来说它不起作用,有帮助吗?我正在尝试使用Nexus 5。 - Kantesh
2
尝试使用棒棒糖版本,但无法运行。只是打开了一个新的“YourActivity”。 - Murphybro2
1
哇,经过数小时的调整标志位,这个解决方案终于奏效了。非常感谢! - farthVader
显示剩余14条评论

49

对于不想/无法硬编码启动器活动的情况,此解决方案可行。

Intent i = getPackageManager()
    .getLaunchIntentForPackage(getPackageName())
    .setPackage(null)
    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, i, 0);

return new NotificationCompat.Builder(context)
        ...
        .setContentIntent(pendingIntent)
        .build();

setPackage(null)部分对我来说非常关键,因为如果没有它,应用程序将无法恢复到先前的任务。我将我的意图与Android启动器的意图进行了比较,并注意到那里没有设置包名称,这就是我想要从意图中删除包名称的原因。

我的特殊情况是通知是在库中创建的,所以我无法知道启动器活动会是什么。


完美运作。但在我的情况下,我需要传递一些数据,在应用程序完全退出并能够在 onCreate - Intent 中获取数据时,它可以正常工作,而当应用程序在后台运行时,我无法从 Intent 中获取数据。请问您能否 - Prat
看起来这是唯一可行的解决方案,但你设置FLAG_ACTIVITY_RESET_TASK_IF_NEEDED有什么作用? - Emil
1
终于有一个符合预期的答案了,它完美地工作!无论如何指定活动都没有意义,所以即使那些其他解决方案可行,这个更清晰明了! - P Kuijpers
有时它不起作用。我有两个不同的活动,A和B。我等待Firebase推送通知,并在其到来后显示正在进行的通知。通过通知单击,我显示活动B并同时显示其他通知。该通知应执行与此主题作者所要求的完全相同的操作,例如仅从上一个活动中恢复应用程序。如果在显示活动B之前未显示活动A,则无法正常工作。但是,如果我在没有先前显示A的情况下显示B,则在通知单击后,我会看到活动A而不是活动B。 - Ivan Syabro
顶部答案或其他任何答案都对我无效。这是唯一有效的! - Bruce
显示剩余2条评论

4

我也遇到了同样的问题,并尝试按照@GrAnd的答案来解决问题:

final Intent notificationIntent = new Intent(context,YourActivity.class);
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);

这个方案是行得通的,毫无疑问,但问题在于当你将意图设置为ACTION_MAIN时。此时,您将无法向意图设置任何bundle。也就是说,目标活动将无法接收到您的基本数据,因为ACTION_MAIN不能包含任何额外数据。

相反,您可以将活动设置为singleTask,并正常调用意图而不设置ACTION_MAIN,并从目标活动的onNewIntent()方法中接收意图。

但如果您调用super.onNewIntent(intent);,则会创建第二个活动实例,请注意。


3

创建一个活动,然后设置相应的类别和标志...

这是我的解决方案,我必须这样做是因为我要支持Api lvl 8

intent.addCategory(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClass(this, YourActivity.class);

intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT|
                Intent.FLAG_ACTIVITY_SINGLE_TOP);

PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 
                   PendingIntent.FLAG_UPDATE_CURRENT);

在 AndroidManifest 文件中

android:launchMode="singleTask"

所以,这个技巧的关键在于使用了Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT标志,并在清单文件中设置了相应的行。希望对其他人有所帮助。

Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT 没有任何作用,不建议使用 android:launchMode="singleTask",即使在方向和其他配置更改期间,活动也不会被重新创建。 - user924
你试过用API lvl 8吗?已经过去6年了,我猜很多东西都改变了。顺便说一句,把它标记为无用太粗鲁了。 - Héctor William

1

终极解决方案:通知恢复任务而不是特定活动?

public class YourRootActivity extends Activity 
    {
        @Override
        protected void onCreate(Bundle savedInstanceState) 
        {
            super.onCreate(savedInstanceState);
    
            if (!isTaskRoot()) // checks if this root activity is at root, if not, we presented it from notification and we are resuming the app from previous open state
            {
                 val extras = intent.extras // do stuffs with extras.
                 finish();
                 return;
            }
             // OtherWise start the app as usual
        }
    }

0

这很简单,打开您的清单文件并在活动属性中设置属性启动模式singleTop


你在哪个API上测试的?它似乎在API 23上无法工作。按下“返回”按钮然后点击通知会启动第二个活动实例。 - behelit

-1
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.REORDER_TASKS" />

private void bringApplicationToForeground(){
    ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

      List<ActivityManager.AppTask> tasksList = am.getAppTasks();
      for (ActivityManager.AppTask task : tasksList){
        task.moveToFront();
      }
    }else{

      List<ActivityManager.RunningTaskInfo> tasksList = am.getRunningTasks(Integer.MAX_VALUE);
      if(!tasksList.isEmpty()){
        int nSize = tasksList.size();
        for(int i = 0; i < nSize;  i++){
          if(tasksList.get(i).topActivity.getPackageName().equals(getPackageName())){
            am.moveTaskToFront(tasksList.get(i).id, 0);
          }
        }
      }
    }
}

-4

可能有更简单的方法,但您可以将数据存储在SQLite数据库中,每当您重新启动应用程序时,您可以检索保存到数据库中的任何状态,并将其设置为所需的值。


2
不,不,不。绝对不行 ;) - finki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接