单击 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 (onCreate
或 onHandleIntent
)。但是,当我最初通过另一个通知启动应用程序后单击通知时,不会调用 InitializationActivity(无论是上述方法还是 onHandleIntent)。
为什么在这种特定情况下会出错,而所有其他通知点击都可以正确处理?我的应用程序是否通过带有 PendingIntent
的通知进行了错误初始化 - 可能是由于使用了标志 - 因此它无法再处理新通知?
当您从 Notification
启动该应用时,如果该应用尚未 运行,Android 将为该应用创建一个新任务并启动 Activity
进入该任务。 Android 记住它用来启动任务的 Intent
。
如果稍后用户从 Notification
启动应用程序,Android 只会将现有任务带到前台,因为用于启动应用程序的 Intent
与 Intent
匹配该任务最初是用 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">
我不完全明白为什么(是的,我确实阅读了这两个标志的文档),所以在评论中给出明确的解释将不胜感激。
我的应用在收到聊天消息的推送消息时显示通知。当我单击该消息时,我想启动应直接转发到特定聊天屏幕(而不是主屏幕)的应用程序(如果尚未启动)。如果该应用程序已存在于该屏幕中,它会更新显示的消息(实际上类似于 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 (onCreate
或 onHandleIntent
)。但是,当我最初通过另一个通知启动应用程序后单击通知时,不会调用 InitializationActivity(无论是上述方法还是 onHandleIntent)。
为什么在这种特定情况下会出错,而所有其他通知点击都可以正确处理?我的应用程序是否通过带有 PendingIntent
的通知进行了错误初始化 - 可能是由于使用了标志 - 因此它无法再处理新通知?
当您从 Notification
启动该应用时,如果该应用尚未 运行,Android 将为该应用创建一个新任务并启动 Activity
进入该任务。 Android 记住它用来启动任务的 Intent
。
如果稍后用户从 Notification
启动应用程序,Android 只会将现有任务带到前台,因为用于启动应用程序的 Intent
与 Intent
匹配该任务最初是用 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">
我不完全明白为什么(是的,我确实阅读了这两个标志的文档),所以在评论中给出明确的解释将不胜感激。