单击 Android 带有附加信息的通知以初始化应用程序或更新数据:当应用程序由先前的通知启动时未处理

Clicking Android notification with extras to initialize app or update data: not handled when app was started by previous notification

我的应用在收到聊天消息的推送消息时显示通知。当我单击该消息时,我想启动应直接转发到特定聊天屏幕(而不是主屏幕)的应用程序(如果尚未启动)。如果该应用程序已存在于该屏幕中,它会更新显示的消息(实际上类似于 WhatsApp)。

目前,这在大多数情况下似乎工作得很好,除了这个:

注意:当应用程序最初以正常方式启动时(因此不是通过通知点击),通知点击始终得到正确处理,无论我收到哪个通知以及我导航到聊天屏幕的频率如何。

一些信息和代码:

我的应用程序有几个活动,其中最重要的是 MainActivity、一个 LoginActivity 和一个 InitializationActivity(后者是启动器并选择应启动其他 2 个中的哪个)。这是相关的 Manifest 部分:

 <activity
     android:name=".activity.InitializationActivity"
     android:launchMode="singleTop">
     <intent-filter>
         <action android:name="android.intent.action.MAIN"/>
         <category android:name="android.intent.category.LAUNCHER"/>
     </intent-filter>
 </activity>
 <activity
     android:name=".activity.LoginActivity"
     android:launchMode="singleTop"/>
 <activity
     android:name=".activity.MainActivity"
     android:launchMode="singleTop"/>

初始化活动:

@Override
protected void onStart()
{
    super.onStart();
    decideStartUpMode();
}

@Override
protected void onNewIntent(Intent intent)
{
    // Just to be sure the intent from a notification is also saved through this method.
    // However, should not be required, because this activity is always destroyed before starting a new one.
    // Doesn't appear to be called in any case (decideStartUpMode is always called through onStart)
    super.onNewIntent(intent);
    setIntent(intent);
    decideStartUpMode();
}

private void decideStartUpMode()
{
    if (!isUserLoggedIn())
    {
        startLoginActivity();
    }
    else
    {
        // Starts some API calls to collect the required user info.
        // When the data is collected we callback to this class and start the MainActivity.
        startInitialization();
    }
}

...

private void startMainActivity()
{
    Intent intentActivityMain = new Intent(this, MainActivity.class);
    intentActivityMain.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

    // Collect (Pending)Intent from eg. notification - if present - and add this to the new activity
    Intent intentInitializationActivity = getIntent();
    intentActivityMain.setAction(intentInitializationActivity.getAction());
    intentActivityMain.setData(intentInitializationActivity.getData());
    Bundle extras = intentInitializationActivity.getExtras();
    if (extras != null)
    { // Extra's from notification
        intentActivityMain.putExtras(extras);
    }

    startActivity(intentActivityMain);
    finish();
}

主要活动:

// INTENT_HANDLER will handle the actions from clicked push notifications

@Override
public void onCreate(@Nullable Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    INTENT_HANDLER.doIntentAction(getIntent(), this); // If activity is started through InitializationActivity
}

@Override
protected void onNewIntent(Intent newIntent)
{
    super.onNewIntent(newIntent);
    INTENT_HANDLER.doIntentAction(newIntent, this); // If activity was already active
}

最后创建带有启动器意图的通知:

public void showChatNotification(Context context, ChatNotification chatNotificationData)
{
    Bundle bundle = new Bundle();
    bundle.putSerializable(CHAT_NOTIFICATION_DATA, chatNotificationData);

    PendingIntent onNotificationClickIntent = getAppLauncherIntent(context, CHAT_NOTIFICATION, notificationId, extras);
    String channelId = // ... also create channel if not exists ...
    NotificationCompat.Builder notification = new NotificationCompat.Builder(getContext(), channelId);
    notification.setContentIntent(onNotificationClickIntent);
    notification.setAutoCancel(true);
    // ... Set other notification data: title, message, etc...
    context.getSystemService(Context.NOTIFICATION_SERVICE) 
           .notify(notificationId.id, notification.build());
}

private PendingIntent getAppLauncherIntent(Context context, String action, NotificationId notificationId, Bundle extras)
{
    Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
    intent.setAction(action);
    intent.putExtras(extras);
    return PendingIntent.getActivity(context, notificationId.id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}

在正常情况下,点击通知总是通过 InitializationActivity (onCreate > onStart > startInitialization > startMainActivity) 处理,然后是 MainActivity (onCreateonHandleIntent)。但是,当我最初通过另一个通知启动应用程序后单击通知时,不会调用 InitializationActivity(无论是上述方法还是 onHandleIntent)。

为什么在这种特定情况下会出错,而所有其他通知点击都可以正确处理?我的应用程序是否通过带有 PendingIntent 的通知进行了错误初始化 - 可能是由于使用了标志 - 因此它无法再处理新通知?

当您从 Notification 启动该应用时,如果该应用尚未 运行,Android 将为该应用创建一个新任务并启动 Activity 进入该任务。 Android 记住它用来启动任务的 Intent
如果稍后用户从 Notification 启动应用程序,Android 只会将现有任务带到前台,因为用于启动应用程序的 IntentIntent 匹配该任务最初是用 AND Intent 包含 FLAG_ACTIVITY_NEW_TASK 启动的。这就是当应用程序最初从 Notification.

启动时您会看到这种奇怪行为的原因

要防止这种行为,您需要将 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 添加到您在 Notification 中使用的 Intent。这将防止 Android 记住用户使用此 Intent.

启动了应用程序

我的问题已通过从

更改清单得到解决
<activity
     android:name=".activity.InitializationActivity"
     android:launchMode="singleTop">

<activity
     android:name=".activity.InitializationActivity"
     android:launchMode="singleTask">

我不完全明白为什么(是的,我确实阅读了这两个标志的文档),所以在评论中给出明确的解释将不胜感激。