通知还原任务而不是特定活动?

38
我有一个前台服务,只要用户登录应用程序,它就会与服务器保持连接。这样可以保持连接的活性,即使用户按下Home键将应用程序置于后台,也能直接从服务器接收消息。
该应用程序有多个Activity,在被置于后台时,任何一个Activity都可能是活动的。
我想允许用户单击通知以恢复当前的Activity。我知道如何恢复特定的Activity,但不确定是否有一种方法可以恢复用户上次所在的Activity?当然,我可以跟踪上次所在的Activity,然后从通知回调中调用它,但想知道是否有一种在任务级别上实现的方法?
感谢您提供的任何建议。
6个回答

78

你所需要的仅仅是一个什么都不做的简单 Activity。这里有一个示例:

public class NotificationActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Now finish, which will drop the user in to the activity that was at the top
        //  of the task stack
        finish();
    }
}

设置通知以启动此活动。请确保在清单中此活动的任务关联与应用程序中其他活动的任务关联相同(如果没有明确设置android:taskAffinity,则默认情况下是相同的)。

当用户选择此通知时,如果您的应用程序正在运行,则NotificationActivity将在应用程序任务中位于最顶部的活动上启动,并将该任务带到前台。当NotificationActivity完成时,它将简单地将用户返回到应用程序中位于最顶部的活动(即:当应用程序进入后台时离开的位置)。

如果您的应用程序尚未运行,则此方法将不起作用。但是,您有两个选项来解决这个问题:

  1. 确保当您的应用程序未运行时,通知栏中不存在该通知。

  2. 在NotificationActivity的onCreate()方法中检查您的应用程序是否正在运行,如果没有运行则调用startActivity()并启动您的应用程序。如果您这样做,请务必在启动应用程序时设置标志Intent.FLAG_ACTIVITY_NEW_TASK,以便任务的根活动不是NotificationActivity。


1
非常好的回答,谢谢@DavidWasser - Antwan
1
功能很好,但是这种hack是解决这个问题的唯一可靠方法,这很愚蠢。无论设置多少意图标志,都无法在API 16+上可靠地从最近的应用程序列表中恢复应用程序。 - Cord Rehn
1
今天这仍然是最好的想法吗? - Archie G. Quiñones
1
@ArchieG.Quiñones 一般来说,更好的解决方案是使用“启动器Intent”,您可以通过调用PackageManager.getLaunchIntentForPackage("my.package.name")来获取它。但是,这种解决方案并不适用于所有情况,如果它不起作用,调试可能会很困难。我的答案中的解决方案应该始终有效,因为它不依赖于特殊的Intent标志或启动模式。 - David Wasser
1
一直在寻找这个。这是需要在文档中的解决方案。非常感谢! - coolcool1994
显示剩余7条评论

36

非常有效,谢谢David! 以下类检查应用程序是否已经在运行,如果没有,则在完成之前启动它(如选项2中所建议的)。

public class NotificationActivity extends Activity 
{
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);

        // If this activity is the root activity of the task, the app is not running
        if (isTaskRoot())
        {
            // Start the app before finishing
            Intent startAppIntent = new Intent(getApplicationContext(), MainActivity.class);
            startAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(startAppIntent);
        }

        finish();
    }
}

isTaskRoot() 方法值得称赞。 - SafaOrhan

13

有一个更简单的解决方案,不需要额外的操作。请参见此文章以获取详细信息。基本上,通知会像在应用程序在后台时单击启动器图标时一样启动(可能存在的)任务。


实际上,这个想法本身在使用Android API 23时对我来说并不可靠。 - SoloPilot

4
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);

这种方法确实有效,没有任何疑问,但问题在于当你将intent设置为ACTION_MAIN时。此时你将无法向intent中添加任何bundle。我的意思是,因为ACTION_MAIN不能包含任何额外数据,所以你的原始数据将无法从目标活动中接收。

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

但要注意,如果你调用super.onNewIntent(intent);那么将会创建第二个活动实例。只需不调用super方法即可。


是的!谢谢您提供这个。 setAction 函数因某些奇怪的原因而需要,以允许不同的通知发送到不同的类,因此您的解决方案是必需的。 - behelit

4

我的解决方案模拟启动器的行为(将任务置于前台):

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClassName(MyApplication.class.getPackage().getName(), MainActivity.class.getName());

0
我把David Wasser和Raginmari的解决方案结合起来,通过对您的应用程序的根活动采取这种方法,它将适用于您的应用程序已经启动或尚未启动的两种情况。
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
        }
    }

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