【问题标题】:Generate int unique id as android notification id生成int唯一id作为android通知id
【发布时间】:2014-11-01 00:25:09
【问题描述】:

当我发送多个推送通知时,我需要将它们全部显示在按发送时间排序的通知栏中。我知道我应该使用唯一通知 - 我尝试生成随机数,但这并没有解决我的问题,因为我需要订购它们。我尝试使用AtomicInt 仍然没有得到想要的结果。

package com.mypackage.lebadagency;
import java.util.concurrent.atomic.AtomicInteger;

import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
import android.util.Log;



import android.widget.RemoteViews;

import com.google.android.gms.gcm.GoogleCloudMessaging;

public class GCMNotificationIntentService extends IntentService {

  private AtomicInteger c = new AtomicInteger(0);
  public int NOTIFICATION_ID = c.incrementAndGet(); 

  private NotificationManager mNotificationManager;
  NotificationCompat.Builder builder;

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

  public static final String TAG = "GCMNotificationIntentService";

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

    String messageType = gcm.getMessageType(intent);

    if (!extras.isEmpty()) {
      if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR
          .equals(messageType)) {
        sendNotification("Send error: " + extras.toString());
      } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED
          .equals(messageType)) {
        sendNotification("Deleted messages on server: "
            + extras.toString());
      } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE
          .equals(messageType)) {

        for (int i = 0; i < 3; i++) {
          Log.i(TAG,
              "Working... " + (i + 1) + "/5 @ "
                  + SystemClock.elapsedRealtime());
          try {
            Thread.sleep(5000);
          } catch (InterruptedException e) {
          }

        }
        Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime());

        sendNotification(""
            + extras.get(Config.MESSAGE_KEY));
        Log.i(TAG, "Received: " + extras.toString());
      }
    }
    GcmBroadcastReceiver.completeWakefulIntent(intent);
  }

  private void sendNotification(String msg) {

    Log.d(TAG, "Preparing to send notification...: " + msg);
    mNotificationManager = (NotificationManager) this
        .getSystemService(Context.NOTIFICATION_SERVICE);
    //here start
    Intent gcmintent = new Intent(this, AppGcmStation.class);
    gcmintent.putExtra("ntitle", msg);
    gcmintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    int requestID = (int) System.currentTimeMillis();
    //here end
    PendingIntent contentIntent = PendingIntent.getActivity(this, requestID,
        gcmintent, PendingIntent.FLAG_UPDATE_CURRENT);

    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
        this).setSmallIcon(R.drawable.ic_launcher)
        .setContentTitle("my title")
        .setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
        .setContentText(msg);
    mBuilder.setAutoCancel(true);
    mBuilder.setTicker(msg);
    mBuilder.setVibrate(new long[] { 1000, 1000, 1000, 1000, 1000 }); 
    mBuilder.setLights(Color.RED, 3000, 3000);
    mBuilder.setContentIntent(contentIntent);
    mBuilder.setDefaults(Notification.DEFAULT_SOUND);



    mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
    Log.d(TAG, "Notification sent successfully.");
  }
}

我需要最好和最简单的方法来生成一个增量 int id,以将其分配为通知 id。

【问题讨论】:

  • “我尝试使用 atomicint 仍然没有得到想要的结果” - 你得到了什么结果,它与想要的结果有什么不同?
  • 当我使用 atomic 时,android 设备会收到所有通知,但通知列表只显示最新发送的通知。

标签: java android push-notification


【解决方案1】:

您为所有通知使用相同的通知 ID(值始终为 1)。您可能应该将通知 ID 分离到单独的单例类中:

public class NotificationID {
    private final static AtomicInteger c = new AtomicInteger(0);
    public static int getID() {
        return c.incrementAndGet();
    }
}

然后在您的代码中使用NotificationID.getID() 而不是NOTIFICATION_ID

编辑:正如@racs 在评论中指出的那样,如果您的应用程序进程碰巧被杀死,上述方法不足以确保正确的行为。至少,AtomicInteger 的初始值应该从某个活动的保存状态初始化,而不是从 0 开始。如果通知 ID 在应用程序重新启动时需要是唯一的(同样,应用程序进程可能会被终止),那么最新的值应该在每次增量后保存在某个地方(可能是共享首选项),并在应用启动时恢复。

【讨论】:

  • 再次感谢您,先生,但您能向我解释为什么它在单独的类中而不在我的代码中工作时有效吗?
  • 您的代码只调用一次incrementAndGet:在初始化NOTIFICATION_ID 时。此后,您只需使用分配的值。此外,每次重新创建服务时,它都会将 NOTIFICATION_ID 初始化为 1。通过将 id 生成器移动到单独的类,它创建的值会在服务本身的生命周期之后持续存在(并持续到内存中的应用程序)。为了真正确保您的应用生成的每个通知都是唯一的,您可能应该将值保存在共享首选项(或其他方便的地方)中,以便它在整个关闭过程中仍然存在。
  • 当原子整数达到最大值时——会不会自动翻转成负整数,从而形成一个不需要进一步措施的无限循环?
  • @Zainodis - 是的,AtomicInteger 的行为与普通的 int 值相同——连续递增最终将从Integer.MAX_VALUE 环绕到Integer.MIN_VALUE。跨度>
  • 这不是一个合适的解决方案:如果您的应用被终止,那么 ID 将重新启动,除非您将之前的状态保存到应用启动之间的某个位置(例如共享首选项)。
【解决方案2】:

对于仍在四处张望的人。我生成了一个时间戳并将其用作 id。

import java.util.Date;
import java.util.Locale;

public int createID(){
   Date now = new Date();
   int id = Integer.parseInt(new SimpleDateFormat("ddHHmmss",  Locale.US).format(now));
   return id;
}

像这样使用它

int id = createID();
mNotifyManager.notify(id, mBuilder.build());

【讨论】:

  • @RaphaelC 您可能想在该格式中添加毫秒,所以它是“ddHHmmssSS”
  • 如果您在同一毫秒内收到多个通知,@PaulFreez 也是如此,但我同意几率很小。在这种情况下,您也只需选择:int id = (int)System.currentTimeMillis(); 保存任何解析处理。
  • @Paul Freez 当然可以。它只会被截断。
  • @marcosE。这不是我的方法,我是在回应。请参阅我的第一个答案:“因此,如果您在同一秒内收到多个通知,它仍然会取消前一个”。
  • 我不推荐这种方法。用户可能会更改系统时钟(或在时区之间移动,或有夏令时)并意外导致 id 冲突。并且在同一秒内发送的多个通知将具有相同的 id。
【解决方案3】:

也许不是最好的,但绝对最简单的是使用当前时间。

int oneTimeID = (int) SystemClock.uptimeMillis();
mNotificationManager.notify(oneTimeID, mBuilder.build());

好处:这是增加 id 的最简单方法。

坏处:时间是long,我们将其截断为一半。这意味着计数器将每 2'147'483'647 /1000(ms->s)/60(s->m)/60(m->h)/24(h->d) =~ 25 天。

SystemClock.uptimeMillis()currentTimeMillis 有两个优势:

  1. 打折在深度睡眠中花费的所有毫秒数,从而减少环绕量。
  2. 手机重启时从0开始。

【讨论】:

  • int oneTimeID = (int) (SystemClock.uptimeMillis() % 99999999);怎么样
  • @زياد 这样会更快。
  • 是的,但这始终是 int,不会像 SystemClock.uptimeMillis() 一样长。
  • @زياد 这就是转换为 (int) 的作用。
  • 这是一个很好的解决方案。此外,如果您想使用一致的 id 降低与其他通知发生冲突的可能性,请尝试将这些 id 保持在一个范围内。 (即 100-500)
【解决方案4】:
private static final String PREFERENCE_LAST_NOTIF_ID = "PREFERENCE_LAST_NOTIF_ID";

private static int getNextNotifId(Context context) {
    SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
    int id = sharedPreferences.getInt(PREFERENCE_LAST_NOTIF_ID, 0) + 1;
    if (id == Integer.MAX_VALUE) { id = 0; } // isn't this over kill ??? hahaha!!  ^_^
    sharedPreferences.edit().putInt(PREFERENCE_LAST_NOTIF_ID, id).apply();
    return id;
}

【讨论】:

  • 共享首选项存储是异步完成的,因此在存储值到实际存储之前,您可能会再次调用此方法,getInt() 将返回与以前相同的 int,此 + 1 将为您提供与上一个通知相同的 id。如果您立即收到推送通知并快速连续调用此方法,则可能会发生这种情况。
  • @AndreiBogdan 不是真的:当调用apply 时,提交到磁盘是异步的,而内存中的数据会立即更新。如果此 SharedPreferences 上的另一个编辑器在 apply() 仍然未完成时执行常规 commit(),则 commit() 将阻塞,直到所有异步提交以及提交本身都完成。 developer.android.com/reference/android/content/… 简而言之,不,这可能不会发生。我还针对这种特殊情况进行了单元测试,您可以轻松地检查自己是否不会发生这种情况。
  • 我可能只是为了好玩才发布这个,但我们必须尊重,您的答案在这种情况下不起作用,您的应用程序发送了数字 Integer.MAX_VALUE 的通知,然后您的 id 将重置为值 0所以它会覆盖第一个 id 为 0 的通知。但这种情况真的永远不会发生,因为你可能在 android 中不能有这么多的通知,但理论上......算了吧,我只是在开玩笑 XD 谢谢你的好回答! PS很好的评论^_^
【解决方案5】:

您可以使用计数器并将其存储在 SharedPreferences 中。 这是 kotlin 中的一个示例:

fun getNextNotificationId(context: Context) : Int {
    val sp = context.getSharedPreferences("your_shared_preferences_key", MODE_PRIVATE)
    val id = sp.getInt("notification_id_key", 0)
    sp.edit().putInt("notification_id_key", (id + 1) % Int.MAX_VALUE).apply()

    return id
}

它将获取 id 并将存储下一个 id(增加 1),如果 id 达到整数的最大值,它将被重置为 0。

你可以这样使用它:

val notificationId = getNextNotificationId(applicationContext)
notificationManager.notify(notificationId, yourNotification)

【讨论】:

    【解决方案6】:

    您需要设置一个唯一的标签(字符串)/id(整数)

    Checkout this method in official documentaation

    我建议在通知时使用任何时间戳(SystemClock.uptimeMillis() / System.currentTimeMillis())作为标签。

     notificationManager.notify(String.valueOf(System.currentTimeMillis()), 0, notification);
    

    【讨论】:

    • 这应该是这种情况的最佳答案,如果我们需要处理推送通知(以编程方式关闭),我们应该跟踪(tag,id)。 IMO 为最佳实践,我们应该为特定目的定义 id 而不是幻数 0
    【解决方案7】:

    如果有人读到这里,有一个方法here。最好将tag 名称也与id 一起指定,这样如果您将该部分捆绑为模块以与开发人员共享,这将有所帮助。

    注意:分配一些随机整数 id 的问题是,如果任何模块或库使用相同的 id,您的通知将被新的通知数据替换。

    // here createID method means any generic method of creating an integer id
    int id = createID();
    // it will uniqly identify your module with uniq tag name & update if present.
    mNotifyManager.notify("com.packagename.app", id, mBuilder.build());
    

    【讨论】:

    • 请说明createID() 是什么,如果没有阅读其他答案会很混乱。
    • @我是蝙蝠侠。谢谢你。现在我想知道这如何回答这个问题?添加标签如何影响订单?而且它并没有解决需要唯一 ID 的原始问题。如果您想将此作为额外信息添加,请考虑将您的答案替换为对答案的评论。
    • @user905686 使用唯一的标签名称说,您的模块(您创建的下载器库),用户应用程序代码中的一些随机数不会更新/取消您的通知
    • @user905686 检查我的回答中的 NOTE
    • @I Am Batman 那么得到相同随机数的概率可以忽略不计。如果您想使用非随机 ID,它可能会有所帮助,但您仍然必须确保用户不使用相同的标签。
    猜你喜欢
    • 1970-01-01
    • 2012-10-10
    • 1970-01-01
    • 2012-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多