2

私は簡単なリマインダーアプリケーションを開発しようとしています。ユーザーがリマインダを作成すると、'AlarmManager 'クラスを通してアラームを設定します。アラームを設定するには、リマインダーの識別子をインテント内のデータベースに入れ、そのインテントを保留中のインテントの中に入れます。アラームが受信されると、リマインダの識別子が受信されたインテントから取得され、データベース内で検索される。AlarmManagerが古いデータで保留中のインテントを送信しています

このアプローチはまったく機能しません。通常は正常に動作しますが、受信したインテントの内部にある識別子が古いもので、すべてのアプリケーションが失敗することがあります。これはランダムに起こり、私はそれを再現するための一貫した方法を見つけることができません。それはランダムな失敗であるので、私はこれで完全に失われています。私が意図の余分に受け取る識別子が私がアラームを設定した識別子ではないので、それは全く不条理です。代わりに、過去に発火した古いアラームの識別子です。

package bembibre.alarmfix.alarms; 

import android.app.AlarmManager; 
import android.app.PendingIntent; 
import android.content.Context; 
import android.content.Intent; 
import android.os.Build; 

import java.util.Calendar; 

import bembibre.alarmfix.database.RemindersDbAdapter; 
import bembibre.alarmfix.logging.Logger; 
import bembibre.alarmfix.utils.GeneralUtils; 

/** 
* Created by Max Power on 12/08/2017. 
*/ 

/** 
* Sets alarms in the operating system for the reminders of this application. 
*/ 
public class ReminderManager { 

    /** 
    * This is the key that identifies a metadata item that is attached to the intent of an alarm of 
    * a reminder for tracking it. 
    */ 
    public static final String EXTRA_ALARM_ID = "extra_alarm_id"; 

    private Context mContext; 
    private AlarmManager mAlarmManager; 

    public ReminderManager(Context context) { 
     mContext = context; 
     mAlarmManager = 
      (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 
    } 

    /** 
    * Part of the code that is responsible for setting an alarm. 
    * 
    * @param taskId data base identifier of the reminder. 
    * @param alarmId number that helps distinguishing each one of the alarms set for a same reminder. 
    * @param when when. 
    */ 
    public void setReminder(long taskId, long alarmId, Calendar when) throws AlarmException { 
     PendingIntent pi = getReminderPendingIntent(taskId, alarmId, PendingIntent.FLAG_UPDATE_CURRENT); 

     try { 
      this.setAlarm(pi, when); 
      Logger.log("An alarm has been set successfully for the reminder at " + GeneralUtils.format(when) + ". Reminder id: " + taskId); 
     } catch (Throwable throwable) { 
      Logger.log("The system doesn't let us to set an alarm for the reminder at " + GeneralUtils.format(when), throwable); 
      throw new AlarmException(); 
     } 
    } 

    /** 
    * Unsets the alarm that would trigger for the reminder with the given database identifier. 
    * When calling this method, the reminder could have been erased from the database and it 
    * wouldn't be a problem. This method is only for unsetting its associated alarm from the 
    * system. 
    * 
    * @param taskId database identifier of the reminder. 
    * @param alarmId number that helps distinguishing each one of the alarms set for a same reminder. 
    * @param date date for logging purposes. 
    */ 
    public void unsetReminder(long taskId, long alarmId, String date) { 
     PendingIntent pi = getReminderPendingIntent(taskId, alarmId, PendingIntent.FLAG_UPDATE_CURRENT); 
     mAlarmManager.cancel(pi); 
     Logger.log("An alarm has been unset successfully for the reminder at " + date + ". Reminder id: " + taskId); 
    } 

    /** 
    * Returns the <code>PendingIntent</code> object that must be used for calling this application 
    * when a reminder's alarm triggers. 
    * 
    * @param taskId the number that identifies the associated reminder in the database. 
    * @param alarmId incremental identifier for each alarm of the same reminder. 
    * @param flag flag that controls the behaviour of the pending intent. 
    * @return the <code>PendingIntent</code> object. 
    */ 
    private PendingIntent getReminderPendingIntent(long taskId, long alarmId, int flag) { 
     Intent i = new Intent(mContext, OnAlarmReceiver.class); 
     i.putExtra(RemindersDbAdapter.KEY_ROWID, taskId); 
     i.putExtra(ReminderManager.EXTRA_ALARM_ID, alarmId); 
     PendingIntent pi = PendingIntent.getBroadcast(mContext, (int)taskId, i, flag); 
     return pi; 
    } 

    /** 
    * Sets the alarm in the operating system. 
    * 
    * @param operation 
    * @param when 
    */ 
    private void setAlarm(PendingIntent operation, Calendar when) throws Throwable { 
     /* 
     * The alarm must be set differently depending on the OS version. Anyway, we need the 
     * pending intent in order to know what was the reminder for which the alarm was fired, so 
     * then the correct notification will be shown. 
     */ 
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 
      // Before Marshmallow, we can do this for setting a reliable alarm. 
      mAlarmManager.set(AlarmManager.RTC_WAKEUP, when.getTimeInMillis(), operation); 
     } else { 
      /* 
      * Starting from Marshmallow, it seems like this is the only way for setting a reliable 
      * alarm. 
      * If we use the "alarm clock" framework, the user will see a icon of an alarm clock. 
      * If we use the setExactAndAllowWhileIdle the user will see nothing, but the OS can 
      * delay alarms at some sort of situations. 
      */ 
      mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, when.getTimeInMillis(), operation); 
     } 
    } 
} 

EDIT:アラーム火災識別子が正しい来て、私は希望の識別子を使用してアプリケーションを開くための通知のための別の保留中の意図を作成するときに問題が発生したときに、それは保留中であり、私が通知に触れるときに使用されるインテント。

private void makeNotification(Long rowId, String title, String body) { 
    android.app.NotificationManager mgr = (android.app.NotificationManager)context.getSystemService(NOTIFICATION_SERVICE); 
    Intent notificationIntent; 

    long notificationId; 
    if (rowId == null) { 
     notificationId = 0; 
     notificationIntent = new Intent(context, ReminderListActivity.class); 
    } else { 
     notificationId = rowId; 
     notificationIntent = new Intent(context, ReminderEditActivity.class); 
     notificationIntent.putExtra(RemindersDbAdapter.KEY_ROWID, rowId); 
    } 
    PendingIntent pi = PendingIntent.getActivity(context, 0, notificationIntent, 
      PendingIntent.FLAG_ONE_SHOT); 

    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context) 
      .setSmallIcon(android.R.drawable.stat_sys_warning) 
      .setContentTitle(title) 
      .setContentText(body) 
      .setContentIntent(pi) 
      .setStyle(new NotificationCompat.BigTextStyle().bigText(body));; 

    Notification note = mBuilder.build(); 

    note.defaults |= Notification.DEFAULT_SOUND; 
    note.flags |= Notification.FLAG_AUTO_CANCEL; 

    // An issue could occur if user ever enters over 2,147,483,647 tasks. (Max int value). 
    // I highly doubt this will ever happen. But is good to note. 
    int id = (int)((long)notificationId); 
    mgr.notify(id, note); 
    NotificationManager.setNotified(context, id); 
} 

EDIT:ここでこれを行うコードがある私は、私はいつも失敗を再現することが可能な少なくとも一つのケースを発見しました。手順:

  • 現在の日付のアラームを作成します。
  • 通知を待つと、現在の日付に設定されているのですぐに通知されます。
  • アラームを削除します。
  • 通知を破棄します(通知は破棄しません)。
  • 現在の日付の別のリマインダを作成します。
  • 通知を待ちます。
  • 通知の中をクリックすると、エラー が再生されます。
+0

あなた自身の答えを受け入れてください。これにより未回答の質問のリストから質問が削除されます。 –

答えて

0
私は上記の投稿失敗を再現するための手順については

、それは私がそれに行くことなく、通知を破棄するときのように思えるが、その保留中の意図はまだ生きているので、私は別のリマインダーを作成するときにシステムが古い保留中を使用していますその内部にある古い意図と意図しており、古い識別子を持っています。

この問題は、PendingIntent.getActivity()(requestCode)メソッドの2番目のパラメータに異なる番号を渡すことで解決されたように見えますが、保留中のインテントはすべて異なり、システムは常に試みるのではなく、古いものを使用してください。要求コードとして、最良の方法は、リマインダーのデータベース識別子を使用することであり、したがって、各リマインダーはそれ自体の保留中のインテントで通知を受ける。

0

あなたの答えはOKです。別の方法として、あなたはまた、代わりにFLAG_ONE_SHOTFLAG_UPDATE_CURRENTを使用するようにコードを変更することができます:

PendingIntent pi = PendingIntent.getActivity(context, 0, notificationIntent, 
     PendingIntent.FLAG_UPDATE_CURRENT); 

これは、既存のPendingIntentで「エキストラ」を上書きします。

+0

問題は、私の場合、ステータスバーに一度に複数の通知を持つことができ、それぞれ独立したデータがあることです。そして、それぞれは、他の通知の意図の余分なものを妨害することなく、独自の異なる機能を持っていなければなりません。 – user3289695

+1

同時に複数の通知が必要な場合は、このソリューションを使用することはできません。毎回ユニークな 'PendingIntent'を作成する必要があります。これはあなた自身の答えで指定したようにユニークな' requestCode'を使って行うことができます。 –

関連する問題