英文:
Opening Android app using notification & getLaunchIntentForPackage doesn't go through LauncherActivity
问题
我正在使用Firebase(FCM)向用户显示推送通知,但遇到了一个奇怪的问题。
我有的代码适用于以下情况(使用FirebaseMessagingService):
- 应用在前台 - 在onReceive()中接收数据并在应用内显示弹窗。
- 应用在后台 - 在onReceive()中接收数据并为用户显示通知。如果点击通知,应用将被带回前台。从这里接收的意图在LauncherActivity中进行处理,随后调用finish()将我带回到已经打开的任何活动。
- 应用完全关闭 - 与后台相同。应用将被启动,意图将在LauncherActivity中处理,然后在那上调用finish()。
以下是有趣的部分:
- 应用完全关闭 -> 通过通知打开它(在LauncherActivity中接收意图) -> 将应用置于后台并发送另一个通知 -> 当点击该通知时,LauncherActivity被完全忽略(不再调用onCreate),我直接回到了先前已经打开的任何活动。此处的意图没有额外的内容或类别。
为什么在这种特定情况下会绕过LauncherActivity?请记住,如果应用最初是正常启动的(而不是通过点击通知启动的),这个过程是正常的。
Intent mainIntent = getPackageManager().getLaunchIntentForPackage(getPackageName());
if (mainIntent != null) {
mainIntent.addCategory(NOTIFICATION_CATEGORY);
mainIntent.putExtra(.........);
}
PendingIntent pendingMainIntent = PendingIntent.getActivity(this, SERVICE_NOTIFICATION_ID, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, context.getString(R.string.default_notification_channel_id));
notificationBuilder.setContentIntent(pendingMainIntent);
//.....icon, color, priority, autoCancel, setDefaults, setWhen, setShowWhen, contentText, setStyle
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
getString(R.string.default_notification_channel_id),
getString(R.string.default_notification_channel),
NotificationManager.IMPORTANCE_HIGH
);
notificationManager.createNotificationChannel(channel);
notificationBuilder.setChannelId(getString(R.string.default_notification_channel_id));
}
notificationManager.notify(SERVICE_NOTIFICATION_ID, notificationBuilder.build());
}
如果有任何想法,我将不胜感激。谢谢。
英文:
I am using Firebase (FCM) to show Push Notifications to the user and I am running into a weird problem.
The code I have works for the following scenarios (using FirebaseMessagingService):
- App in foreground - Receiving data in onReceive() and showing a popup inside app.
- App in background - Receiving data in onReceive() and showing a notification for the user. If this is clicked the app will be brought back to front. The intent from this is received in LauncherActivity followed by a finish() call which takes me to whatever activity I already had open.
- App completely closed - same as background. App will be started and intent will be handled in LauncherActivity before calling finish() on that.
And here is where it gets interesting:
- App completely closed -> open it through notification (intent received in LauncherActivity) -> put the app in background and send another notification -> when this notification is clicked the LauncherActivity is completely ignored (onCreate is no longer called) and I get taken straight to whatever activity I already had. The intent here has no extras or categories.
Why is LauncherActivity being bypassed in this specific case? Keep in mind that this works fine if the app was initially started normally (not by clicking on a notification)
Intent mainIntent = getPackageManager().getLaunchIntentForPackage(getPackageName());
if (mainIntent != null) {
mainIntent.addCategory(NOTIFICATION_CATEGORY);
mainIntent.putExtra(.........);
}
PendingIntent pendingMainIntent = PendingIntent.getActivity(this, SERVICE_NOTIFICATION_ID, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, context.getString(R.string.default_notification_channel_id));
notificationBuilder.setContentIntent(pendingMainIntent);
//.....icon, color, pririty, autoCancel, setDefaults, setWhen, setShowWhen, contentText, setStyle
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
getString(R.string.default_notification_channel_id),
getString(R.string.default_notification_channel),
NotificationManager.IMPORTANCE_HIGH
);
notificationManager.createNotificationChannel(channel);
notificationBuilder.setChannelId(getString(R.string.default_notification_channel_id));
}
notificationManager.notify(SERVICE_NOTIFICATION_ID, notificationBuilder.build());
}
I'd appreciate any ideas. Thank you.
答案1
得分: 1
当您首次启动应用程序时,Android会记住用于启动应用程序的“Intent”。通常情况下,当您从主屏幕启动应用程序时,这是一个包含ACTION=MAIN和CATEGORY=LAUNCHER的“Intent”。如果您的应用程序随后进入后台(无论出于何种原因),用户稍后在主屏幕上点击图标,则会使用相同的启动“Intent”。Android将此与首次启动应用程序时使用的“Intent”进行匹配,如果匹配,则不会启动新的“Activity”,而只是将包含应用程序的任务从后台移到前台,处于移至后台时的状态。在正常情况下,这正是您想要的行为(也是用户期望的行为)。
然而,当应用程序首次从“通知”中启动时,情况可能会变得混乱。在您的情况下,这就是您所看到的情况。您从“通知”中启动应用程序,Android会记住使用的“Intent”(来自“通知”),当您稍后再次从“通知”中启动应用程序时,Android会将“通知”中的“Intent”与首次启动应用程序时使用的“Intent”进行匹配,并认为您希望将现有的应用程序任务从后台移到前台。
有几种处理这种情况的方法,取决于您想要实现的行为。最好的做法可能是不要从“通知”中启动您的根“Activity”(具有ACTION=MAIN和CATEGORY=LAUNCHER的那个)。而是启动一个不同的“Activity”,并让该“Activity”确定接下来应该做什么(例如:重定向到根“Activity”或其他操作,取决于您的应用程序状态)。您还应该在放入“通知”的“Intent”上设置“NO_HISTORY”和“EXCLUDE_FROM_RECENTS”标志。这将确保Android不会将此“Intent”记为启动应用程序的“Intent”。
英文:
When you launch an app for the first time, Android remembers the Intent
that was used to launch it. Normally, when you launch an app from the HOME screen, this is an Intent
that contains ACTION=MAIN and CATEGORY=LAUNCHER. If your app then goes to the background (for whatever reason), and the user later taps the icon on the HOME screen, the same launch Intent
is used. Android matches this against the Intent
used to launch the app for the first time, and if these match, Android doesn't launch a new Activity
, it just brings the task containing the app from the background to the foreground in whatever state it was in when it got moved to the background. Under normal circumstances, this is exactly the behaviour that you want (and that the user expects).
However, when the app is launched for the first time from a Notification
, this can mess things up. In your case, this is what you are seeing. You launch the app from a Notification
and Android remembers the Intent
used (from the Notification
), when you later launch the app (again from a Notification
), android matches the Intent
in the Notification
with the Intent
used to launch the app for the first time, and thinks you want to bring the existing app task from the background to the foreground.
There are several ways to deal with this, depending on the behaviour that you want to have. The best thing to do is probably not to launch your root Activity
(the one with ACTION=MAIN and CATEGORY=LAUNCHER) from the Notification
. Instead launch a different Activity
and have that Activity
determine what it should do next (ie: redirect to the root Activity
or something else, depending on the state of your app). You should also set the NO_HISTORY
and EXCLUDE_FROM_RECENTS
flags on the Intent
that you put in the Notification
. This will ensure that Android won't remember this Intent
as the one that launched the app.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论