当安装了多个收听同一个 GCM 频道的应用程序时,如何确保通知只显示一次?

When multiple apps which listen to same GCM channel are installed, How to make sure the notification is shown only once?

我要发布一系列应用程序,我希望能够向通过 GCM 安装这些应用程序的用户显示通知。但是当每台设备上安装了多个应用程序时,会向用户显示多个通知。我想确保只向用户显示一条通知,无论他们安装了多少个应用程序。

我尝试在我发送的每条 GCM 消息中放置一个唯一 ID,并在收到广播消息时在应用程序中将其保存在内部存储中特定文件夹中的文件中,这样我就可以跟踪消息并避免显示来自多个应用程序的相同消息。但我认为这是不可靠的,应该有更好的方法。

所有应用程序都从同一个 GCM 频道接收消息。

我最终使用外部存储上的文件作为检查当前 GCM 消息是否被处理的一种方式,我对其进行了彻底的测试并投入使用。

public class GcmIntentService extends IntentService {
public static final int NOTIFICATION_ID = -3;
private NotificationManager mNotificationManager;
public static final String TAG = "CGMIntentService";

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

@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.
    String messageType = gcm.getMessageType(intent);

    if (!extras.isEmpty()) {  // has effect of unparcelling Bundle
    if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
            // Post notification of received message.
            sendNotification("Received: " + extras.toString(), extras);
            Log.i(TAG, "Received: " + extras.toString());
        }
    }
    // Release the wake lock provided by the WakefulBroadcastReceiver.
    GcmBroadcastReceiver.completeWakefulIntent(intent);
}

private void sendNotification(String msg, final Bundle bundle) {
    ATImg atImg = new ATImg();
    //use a file lock as way to check whether another app is displaying this gcm message
    String fileName = DirectoryHelper.getMCDirectory() + "/" + DirectoryHelper.handledIdsFileName;
    if (DirectoryHelper.isFileLocked(fileName))
        return;//file is locked, it means another app has caught this gcm message prior to this app
    try {
        atImg.randomAccessFile = new RandomAccessFile(fileName, "rw");
        atImg.lock= atImg.randomAccessFile.getChannel().lock();// lock the file so no other app access it
    } catch (FileNotFoundException e) {//again this means file is locked
        e.printStackTrace();
        return;
    } catch (IOException e) {
        e.printStackTrace();
        return;
    }
    atImg.bundle = bundle;
    atImg.doInBackground("");
}

public class ATImg extends AsyncTask<String, String, String> {
    Bundle bundle;
    RandomAccessFile randomAccessFile;
    FileLock lock;
    @Override
    protected void onPostExecute(String reult){
//release the lock on file
        try {
            lock.release();
            randomAccessFile.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    protected String doInBackground(String... strings) {
        String id = bundle.getString("id");
 //CHECK IF THE MESSAGE IS HANDLED

        Gson gson = new Gson();
        List<String> ids = null;
        try {
            Type concerete = new TypeToken<List<String>>() {
            }.getType();
            randomAccessFile.seek(0);
            ids = gson.fromJson(randomAccessFile.readUTF(), concerete);
            if (ids != null && ids.contains(id)) {
                return "";//the message has already been shown
            }
        }
        catch (Exception ex){
            ex.printStackTrace();
            return "";
        }

        //write the new id to the end of the file
        if (ids == null)
            ids = new ArrayList<>();
        ids.add(id);
        try {
            randomAccessFile.seek(0);
            randomAccessFile.writeUTF(gson.toJson(ids));
        } catch (IOException e) {
            e.printStackTrace();
        }
 //END MASSAGE HANDLING CODE
 //write code to show the message
    }
}
}