关于推送通知:Android GCM和多个令牌

Android GCM and multiple tokens

我在GCM中使用GoogleCloudMessaging.getInstance(context)注册; 并将收到的令牌保存在设备上。 然后将其发送到服务器,它与用户帐户相关联。 如果我卸载我的应用程序而没有注销并再次安装并与其他用户一起登录,我会收到新令牌并将其发送到服务器。 当推送发送给第一个用户时,我在第二个用户登录时看到它们。

为什么GCM会向我发送不同的令牌,我该如何处理?


欢迎来到Google Cloud Messaging重复消息的奇妙世界。发生这种情况时,GCM引擎启用Canonical IDs来解决它。这可能是因为您为同一设备注册了多个ID,或者因为GCM服务器在卸载应用程序时未获得unregister()调用。使用规范ID会将您的ID设置为您最后一次注册。

根据GCM reference关于此:

Canonical IDs

On the server side, as long as the application is behaving well, everything should work normally. However, if a bug in the application triggers multiple registrations for the same device, it can be hard to reconcile state and you might end up with duplicate messages.

GCM provides a facility called"canonical registration IDs" to easily recover from these situations. A canonical registration ID is defined to be the ID of the last registration requested by your application. This is the ID that the server should use when sending messages to the device.

If later on you try to send a message using a different registration ID, GCM will process the request as usual, but it will include the canonical registration ID in the registration_id field of the response. Make sure to replace the registration ID stored in your server with this canonical ID, as eventually the ID you're using will stop working.

更多信息在这里。

还有一个关于如何开始的实际案例,它可能会有所帮助:

  • 规范注册ID和消息ID格式

我在卸载应用程序时遇到了注册ID更改,尝试在卸载时向应用程序发送消息(直到我收到NotRegistered错误),然后重新安装。

来自Google的Costin Manolache建议以这种方式处理注册ID更改:

The suggestion/workaround is to generate your own random identifier, saved as a shared preference for example. On each app upgrade you can upload the identifier and the potentially new registration ID. This may also help tracking and debugging the upgrade and registration changes on server side.

当然,这仅在应用程序保持安装时才有效(因为应用程序删除了共享首选项)。但是,如果设备具有外部存储,则可以在其中存储标识符,并在再次安装应用程序时,从外部存储加载存储的标识符。这样您就会知道新的注册ID和旧注册ID属于同一设备。

此外,您应该在服务器中处理来自Google的规范注册ID响应,如其他答案中所述。


您可以发送Android设备ID以及注册ID。 Android设备ID是唯一的,在应用程序卸载重新安装期间保持不变,只有在设备出厂重置时才会更改。

示例:如何在Android中获取唯一的设备硬件ID?


首先,当您发送通知时,用它发送用户ID并询问sharedpreference == comming中的id是否为no

如果您向所有用户发送通知,并且可能是某人获得2通知,而他应该只有一个人这样做

在您的服务器上创建文件并使用任何数字表示0然后当您要发送通知时发送此数据然后在此数字中添加一个++
在下一个通知中成为新号码,与每个新号码相同

在android应用程序中添加变量并在添加通知后让此变量=来自服务器的comming变量
但你需要问if(number_in_your_service!=server_number)
//添加通知

将显示您发送的任意数量的通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class GcmIntentService extends IntentService {
  public static int openintent;
  public static final int NOTIFICATION_ID = 1;
  private static final String TAG ="GcmIntentService";

  private static String number_in_your_service="somethingneversend";
  NotificationCompat.Builder builder;

  public GcmIntentService() {
    super("GcmIntentService");
  }

  @Override
  protected void onHandleIntent(Intent intent) {
    Bundle extras = intent.getExtras();
    GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
    String messageType = gcm.getMessageType(intent);

    if (!extras.isEmpty()) { // has effect of unparcelling Bundle
      if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
      } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
        // If it's a regular GCM message, do some work.
      } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
        // This loop represents the service doing some work.
        for (int i = 0; i < 5; i++) {
          Log.i(TAG,"Working..." + (i + 1) +"/5 @" + SystemClock.elapsedRealtime());
          try {
            Thread.sleep(100);
          } catch (InterruptedException e) {
          }
        }
        Log.i(TAG,"Completed work @" + SystemClock.elapsedRealtime());
        // Post notification of received message.
        sendNotification(extras);
        Log.i(TAG,"Received:" + extras.toString());
      }
    }
    // Release the wake lock provided by the WakefulBroadcastReceiver.
    GcmBroadcastReceiver.completeWakefulIntent(intent);
  }

  private void sendNotification(Bundle extras) {

    if((extras.getString("server_number")).equals(number_in_your_service)) {

      Intent intent = new Intent(this, Main_Page_G.class);
      intent.putExtra("frame",100);
      intent.putExtra("bundle",extras);
      final PendingIntent contentIntent = PendingIntent.getActivity(this,
            120, intent, PendingIntent.FLAG_UPDATE_CURRENT);
      NotificationManager mNotificationManager;
      NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
            GcmIntentService.this).setContentTitle("name")
            .setContentText("content")
            .setDefaults(Notification.DEFAULT_SOUND)
            .setContentInfo("Test")
            .setSmallIcon(R.drawable.rehablogo2)
            .setAutoCancel(true);
      mBuilder.setContentIntent(contentIntent);
      mNotificationManager = (NotificationManager) GcmIntentService.this
            .getSystemService(Context.NOTIFICATION_SERVICE);
      mNotificationManager.notify(id, mBuilder.build());
      id=Integer.parseInt(extras.getString("id"));
    }
  }
}

向多个设备发送推送通知与我们发送到单个设备的相同。
只需将所有已注册设备的注册令牌存储到您的服务器即可。
并且当使用curl调用推送通知时(我假设您使用php作为服务器端)将所有注册ID放入数组中。
这是一个示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?php
//Define your GCM server key here
define('API_ACCESS_KEY', 'your server api key');

//Function to send push notification to all
function sendToAll($message)
{
    $db = new DbOperation();
    $tokens = $db->getAllToken();
    $regTokens = array();
    while($row = $tokens->fetch_assoc()){
        array_push($regTokens,$row['token']);
    }
    sendNotification($regTokens,$message);
}


//function to send push notification to an individual
function sendToOne($email,$message){
    $db = new DbOperation();
    $token = $db->getIndividualToken($email);
    sendNotification(array($token),$message);
}


//This function will actually send the notification
function sendNotification($registrationIds, $message)
{
    $msg = array
    (
        'message' => $message,
        'title' => 'Android Push Notification using Google Cloud Messaging',
        'subtitle' => 'www.simplifiedcoding.net',
        'tickerText' => 'Ticker text here...Ticker text here...Ticker text here',
        'vibrate' => 1,
        'sound' => 1,
        'largeIcon' => 'large_icon',
        'smallIcon' => 'small_icon'
    );

    $fields = array
    (
        'registration_ids' => $registrationIds,
        'data' => $msg
    );

    $headers = array
    (
        'Authorization: key=' . API_ACCESS_KEY,
        'Content-Type: application/json'
    );

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'https://android.googleapis.com/gcm/send');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
    $result = curl_exec($ch);
    curl_close($ch);

    $res = json_decode($result);

    $flag = $res->success;
    if($flag >= 1){
        header('Location: index.php?success');
    }else{
        header('Location: index.php?failure');
    }
}

在上面的代码中,我们从mysql表中获取注册令牌。要发送到所有设备,我们需要所有令牌。要发送单个设备,我们只需要该设备的令牌。

来源:Google Cloud Messaging示例