应用关闭后未收到推送

Pushes not received after the app is closed

在将其标记为重复之前:

我至少阅读了 15 个类似的线程,每个线程要么使用旧的 Parse 代码(现已弃用的 setDefaultPushCallback),要么问题是调用 Parse.initialize(...)在 activity 而不是在应用程序 class 中。但这适用于我的情况。官方的例子(我用的)显然做对了,所以代码已经在Application class.

我已经下载了 Push Starter example from Parse's official guides 并在模拟器上试用了它。我仅在应用 运行 时收到推送。在它关闭的那一刻(从 "recent apps" 列表中删除,而不是强制杀死),我不再收到推送。这使得整个功能相当无用...我尝试使用和不使用 GCM,行为是相同的。

任何可能出错的线索?所有 classes 都是常用示例,我没有覆盖或添加任何内容(除了我从指南中复制的 id/key 和 ParsePush.subscribeInBackground 调用)。奇怪的是,示例代码没有包含 ParsePush.subscribeInBackground 而 QuickStart 也没有提到它。它甚至提供了一个测试按钮,据称可以发送一个我从未收到过的推送,无论有没有 subscribeInBackground。到目前为止,我能够获得推送的唯一方法是使用 subscribeInBackground 并通过 Web 控制台手动发送推送,并且仅当应用程序为 运行 时才如此。 Web 控制台还不断提示有 2 个已注册设备...这是不正确的。

清单:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.parse.starter"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="21"/>

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <!--
      IMPORTANT: Change "com.parse.starter.permission.C2D_MESSAGE" in the lines below
      to match your app's package name + ".permission.C2D_MESSAGE".
    -->
    <permission android:protectionLevel="signature"
        android:name="com.parse.starter.permission.C2D_MESSAGE" />
    <uses-permission android:name="com.parse.starter.permission.C2D_MESSAGE" />

    <application
        android:name=".ParseApplication"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:allowBackup="true">
        <activity
            android:name=".ParseStarterProjectActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="com.parse.PushService" />
        <receiver android:name="com.parse.ParseBroadcastReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.USER_PRESENT" />
            </intent-filter>
        </receiver>
        <receiver android:name="com.parse.ParsePushBroadcastReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="com.parse.push.intent.RECEIVE" />
                <action android:name="com.parse.push.intent.DELETE" />
                <action android:name="com.parse.push.intent.OPEN" />
            </intent-filter>
        </receiver>
        <receiver android:name="com.parse.GcmBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

                <!--
                  IMPORTANT: Change "com.parse.starter" to match your app's package name.
                -->
                <category android:name="com.parse.starter" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

解析应用程序:

package com.parse.starter;
...

public class ParseApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        // Initialize Crash Reporting.
        ParseCrashReporting.enable(this);

        // Enable Local Datastore.
        Parse.enableLocalDatastore(this);

        ParseUser.enableAutomaticUser();

        // Add your initialization code here
        Parse.initialize(this, "***", "***");

        ParseACL defaultACL = new ParseACL();
        // Optionally enable public read access.
        // defaultACL.setPublicReadAccess(true);
        ParseACL.setDefaultACL(defaultACL, true);

        ParsePush.subscribeInBackground("", new SaveCallback() {
            @Override
            public void done(ParseException e) {
                if (e == null) {
                    Log.d("com.parse.push", "successfully subscribed to the broadcast channel.");
                } else {
                    Log.e("com.parse.push", "failed to subscribe for push", e);
                }
            }
        });
    }
}

为了阐明您看到此行为的原因,Parse 有两种不同的方式来传送推送通知:

  1. "Parse way":Parse SDK 在您的应用程序中有一个组件 运行,它保持与 Parse 后端服务器的连接。这仅在您的应用实际上是 运行 时有效,因为终止它会断开与 Parse 后端的连接。
  2. GCM "Google" 推送通知:这通过 Google Play Services 起作用,该应用程序始终 运行 在后台运行,并且可以在需要时启动您的应用程序。这将始终有效,除非您强行停止应用程序。

在您的情况下,存在包名称冲突:com.parse.starter 是示例中实际包含的包名称。这会导致 GCM 无法工作,因为它已经知道具有不同签名的包。将您的包名称更改为独特的名称,例如 com.parse.kaqqao 应该可以解决这个问题。

这有几个原因:

  1. 有两个 BroadcastReceiver,即 "com.parse.ParsePushBroadcastReceiver" 和 "com.parse.GcmBroadcastReceiver"。我相信第一个接收器的优先级高于 GCMBroadcastReceiver,因此该行为不会受到移除或保留此接收器的影响。也可能是由于操作 "com.parse.push.intent.RECEIVE",它可能正在处理推送消息 RECEIVE 操作。如果两个接收者执行相同的解析推送消息的任务(在后台启动相同的服务),则在一个接收者中包含 intent-filter 并让它处理各种推送消息。由于 GCMBroadcastReceiver 持有 C2DM 权限。

  2. 尝试更改清单中两个广播接收器标签的顺序。 (保持GCMBroadcastReceiver在ParsePushBroadcastReceiver之前)

  3. 可能是由于android:exported="false",可能会阻止Receiver监听服务器发送的推送消息。试试改成真。