收到 GCM 消息但未显示 - 广播接收器结果=取消 - 服务强制停止
GCM message received but not displayed - Broadcast receiver result=CANCELLED - Service force stop
我已经在我的应用程序和服务器上实现了 GCM 消息传递,一切正常,但不是每个 phone!
主要问题是:
当应用程序打开时,我收到 GCM 消息并按预期生成通知,但是当应用程序关闭时,我可以看到(通过 LOGCAT)消息已收到但取消了 -> "Broadcast receiver, result=CANCELLED".
接下来是我无法让我的应用程序服务在后台运行。只要我从 "recent activities" 菜单 "close" 我的应用程序它也会在后台停止(在设置下 -> 应用程序 -> "MY_APP" -> 强制停止被禁用)。
我做了很多研究,我认为原因可能是华为 P8(主要测试设备)在您从 "recent activities" 选项卡上滑动时自动强行关闭所有应用程序。但如果是这种情况,其他应用程序如 "Facebook"、"Viber" 等将不会向我发送推送通知。
Manifest.xml
<receiver
android:name="com.happyhour.gcm.GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.REBOOT"/>
<action android:name="android.intent.action.PACKAGE_REPLACED"/>
<action android:name="android.intent.action.BATTERY_CHANGED"/>
<action android:name="android.intent.action.USER_PRESENT"/>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.happyhour.main" />
</intent-filter>
</receiver>
<service
android:name="com.happyhour.gcm.GcmIntentService" >
</service>
<receiver android:name="com.happyhour.gcm.UpdateReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_INSTALL"/>
<data android:path="com.happyhour.main"
android:scheme="package" />
</intent-filter>
</receiver>
GCMBroadCastReceiver
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.i("GcmBroadcast",intent.getExtras().toString());
SharedPreferences settings = context.getSharedPreferences("MyPrefsFile", 0);
if (settings.getBoolean("my_first_time", true)) {
//the app is being launched for first time, do something
//do nothing
// record the fact that the app has been started at least once
settings.edit().putBoolean("my_first_time", false).commit();
}else{
ComponentName comp = new ComponentName(context.getPackageName(),
GcmIntentService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
}
GCMIntentService
public class GcmIntentService extends IntentService {
private String TAG = "GcmIntent";
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
public GcmIntentService() {
super("GcmIntentService");
}
@Override
public void onCreate(){
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onHandleIntent(intent);
return START_STICKY;
}
@Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
// The getMessageType() intent parameter must be the intent you received
// in your BroadcastReceiver.
Log.i(TAG, "Received: " + extras.toString());
String messageType = gcm.getMessageType(intent);
Log.i(TAG, "Message type:" + messageType);
if (!extras.isEmpty()) { // has effect of unparcelling Bundle
/*
* Filter messages based on message type. Since it is likely that GCM
* will be extended in the future with new message types, just ignore
* any message types you're not interested in, or that you don't
* recognize.
*/
if (GoogleCloudMessaging.
MESSAGE_TYPE_MESSAGE.equals(messageType)) {
Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime());
// Post notification of received message.
sendNotification(extras.getString("message"), extras.getString("title"));
Log.i(TAG, "Received: " + extras.toString());
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
// Put the message into a notification and post it.
// This is just one simple example of what you might choose to do with
// a GCM message.
private void sendNotification(String msg, String title) {
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
if(title.isEmpty()){
title="Happy Hour plus";
}
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, SplashScreen.class), 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_notification)
.setContentTitle(title)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(msg))
.setContentText(msg)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setVibrate(new long[] {250,250,250,250})
.setAutoCancel(true);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
public boolean checkApp(){
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
// get the info from the currently running task
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
if (componentInfo.getPackageName().equalsIgnoreCase("com.happyhour.main")) {
return true;
} else {
return false;
}
}
}
我很困惑!我找不到原因。
提前致谢。
您使用的是非常旧的库。考虑切换到最新的官方 GCM 库。让您的侦听器扩展 GcmListenerService。查找相同的官方文档。而且它更容易维护。
确保正确完成下面提到的所有 TODO:
- 在GoogleDeveloper Console中注册项目,使用唯一的项目名称和包名称,您的应用包名称和应用项目名称必须与控制台中的相匹配。
- 将google_services.json配置文件放在app/目录下。打开它并验证文件中的 API_KEY 是否与 Google 开发人员控制台中配置中显示的匹配,您的包名称是否匹配。同时检查 google 云消息的状态必须是 2.
- 按照 Set up a GCM Client App on Android 中的详细步骤进行操作。必须正确实施所有侦听器。
- 一旦您的应用 运行,记录生成的 GCM 注册令牌,将其发送到您的服务器并交叉检查在 adb 控制台中登录的令牌与在服务器上收到的令牌,两者必须匹配。
- 如果仍然无效,请使用 this 示例应用作为参考。您可以复制侦听器、RegistrationIntentService.java 文件和清单,但不要忘记将包名称更改为您的包名称(在 AndroidManifest.xml 包名称引用中也是如此,专门针对侦听器)
看来华为的 EMUI 界面(在我的例子中是 P8)有一种特殊的处理方式。
安装每个应用程序后,它会提示一个切换按钮,询问您是否允许该特定应用程序在后台运行。
因此,如果应用直接来自 Google Play 商店,它不会提示您这个 "Run in background" 允许按钮,因为它是一个 "trusted source"。
如果您只是从 Android Studio 导出您的 .apk 并将其直接安装到您的 phone 它会提示如上所示的按钮,因为直接安装在您的 phone属于"untrusted source".
的情况
最后,如果您使用 USB 调试器选项在 phone 上安装 .apk,它永远不会提示该按钮,它总是像您按下 "off".每次关闭应用程序时,它都会强制关闭其所有任务。
所以,我所做的是将我的 Android-Studio 生成的 .apk 转移到我的 phone 中并手动安装。当它提示按钮时,我将其切换为 "ON" 就是这样。我终于可以在应用关闭时收到 GCM 消息了![=11=]
我认为这是 EMUI bug/special-app-handling 的问题。
特别感谢@TeChNo_DeViL的帮助。
我已经在我的应用程序和服务器上实现了 GCM 消息传递,一切正常,但不是每个 phone!
主要问题是:
当应用程序打开时,我收到 GCM 消息并按预期生成通知,但是当应用程序关闭时,我可以看到(通过 LOGCAT)消息已收到但取消了 -> "Broadcast receiver, result=CANCELLED".
接下来是我无法让我的应用程序服务在后台运行。只要我从 "recent activities" 菜单 "close" 我的应用程序它也会在后台停止(在设置下 -> 应用程序 -> "MY_APP" -> 强制停止被禁用)。
我做了很多研究,我认为原因可能是华为 P8(主要测试设备)在您从 "recent activities" 选项卡上滑动时自动强行关闭所有应用程序。但如果是这种情况,其他应用程序如 "Facebook"、"Viber" 等将不会向我发送推送通知。
Manifest.xml
<receiver
android:name="com.happyhour.gcm.GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.REBOOT"/>
<action android:name="android.intent.action.PACKAGE_REPLACED"/>
<action android:name="android.intent.action.BATTERY_CHANGED"/>
<action android:name="android.intent.action.USER_PRESENT"/>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.happyhour.main" />
</intent-filter>
</receiver>
<service
android:name="com.happyhour.gcm.GcmIntentService" >
</service>
<receiver android:name="com.happyhour.gcm.UpdateReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_INSTALL"/>
<data android:path="com.happyhour.main"
android:scheme="package" />
</intent-filter>
</receiver>
GCMBroadCastReceiver
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.i("GcmBroadcast",intent.getExtras().toString());
SharedPreferences settings = context.getSharedPreferences("MyPrefsFile", 0);
if (settings.getBoolean("my_first_time", true)) {
//the app is being launched for first time, do something
//do nothing
// record the fact that the app has been started at least once
settings.edit().putBoolean("my_first_time", false).commit();
}else{
ComponentName comp = new ComponentName(context.getPackageName(),
GcmIntentService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
}
GCMIntentService
public class GcmIntentService extends IntentService {
private String TAG = "GcmIntent";
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
public GcmIntentService() {
super("GcmIntentService");
}
@Override
public void onCreate(){
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onHandleIntent(intent);
return START_STICKY;
}
@Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
// The getMessageType() intent parameter must be the intent you received
// in your BroadcastReceiver.
Log.i(TAG, "Received: " + extras.toString());
String messageType = gcm.getMessageType(intent);
Log.i(TAG, "Message type:" + messageType);
if (!extras.isEmpty()) { // has effect of unparcelling Bundle
/*
* Filter messages based on message type. Since it is likely that GCM
* will be extended in the future with new message types, just ignore
* any message types you're not interested in, or that you don't
* recognize.
*/
if (GoogleCloudMessaging.
MESSAGE_TYPE_MESSAGE.equals(messageType)) {
Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime());
// Post notification of received message.
sendNotification(extras.getString("message"), extras.getString("title"));
Log.i(TAG, "Received: " + extras.toString());
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
// Put the message into a notification and post it.
// This is just one simple example of what you might choose to do with
// a GCM message.
private void sendNotification(String msg, String title) {
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
if(title.isEmpty()){
title="Happy Hour plus";
}
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, SplashScreen.class), 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_notification)
.setContentTitle(title)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(msg))
.setContentText(msg)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setVibrate(new long[] {250,250,250,250})
.setAutoCancel(true);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
public boolean checkApp(){
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
// get the info from the currently running task
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
if (componentInfo.getPackageName().equalsIgnoreCase("com.happyhour.main")) {
return true;
} else {
return false;
}
}
}
我很困惑!我找不到原因。
提前致谢。
您使用的是非常旧的库。考虑切换到最新的官方 GCM 库。让您的侦听器扩展 GcmListenerService。查找相同的官方文档。而且它更容易维护。
确保正确完成下面提到的所有 TODO:
- 在GoogleDeveloper Console中注册项目,使用唯一的项目名称和包名称,您的应用包名称和应用项目名称必须与控制台中的相匹配。
- 将google_services.json配置文件放在app/目录下。打开它并验证文件中的 API_KEY 是否与 Google 开发人员控制台中配置中显示的匹配,您的包名称是否匹配。同时检查 google 云消息的状态必须是 2.
- 按照 Set up a GCM Client App on Android 中的详细步骤进行操作。必须正确实施所有侦听器。
- 一旦您的应用 运行,记录生成的 GCM 注册令牌,将其发送到您的服务器并交叉检查在 adb 控制台中登录的令牌与在服务器上收到的令牌,两者必须匹配。
- 如果仍然无效,请使用 this 示例应用作为参考。您可以复制侦听器、RegistrationIntentService.java 文件和清单,但不要忘记将包名称更改为您的包名称(在 AndroidManifest.xml 包名称引用中也是如此,专门针对侦听器)
看来华为的 EMUI 界面(在我的例子中是 P8)有一种特殊的处理方式。
安装每个应用程序后,它会提示一个切换按钮,询问您是否允许该特定应用程序在后台运行。
因此,如果应用直接来自 Google Play 商店,它不会提示您这个 "Run in background" 允许按钮,因为它是一个 "trusted source"。
如果您只是从 Android Studio 导出您的 .apk 并将其直接安装到您的 phone 它会提示如上所示的按钮,因为直接安装在您的 phone属于"untrusted source".
的情况
最后,如果您使用 USB 调试器选项在 phone 上安装 .apk,它永远不会提示该按钮,它总是像您按下 "off".每次关闭应用程序时,它都会强制关闭其所有任务。
所以,我所做的是将我的 Android-Studio 生成的 .apk 转移到我的 phone 中并手动安装。当它提示按钮时,我将其切换为 "ON" 就是这样。我终于可以在应用关闭时收到 GCM 消息了![=11=]
我认为这是 EMUI bug/special-app-handling 的问题。
特别感谢@TeChNo_DeViL的帮助。