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); 
     } 
    } 
} 

편집 : 내가 원하는 식별자로 응용 프로그램을 여는 통지에 대한 또 다른 대기 의도를 만들 때 문제가 발생하면 경보 화재는, 그러나, 그것은 보류에있을 때 식별자가 올바른 온다 알림을 터치 할 때 사용되는 의도입니다. 다음은이 작업을 수행하는 코드입니다.

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); 
} 

편집 : 나는 항상 오류를 재현 할 수있는 적어도 하나의 사례를 발견했습니다. 단계 :

  • 현재 날짜에 대한 알림을 생성합니다.
  • 알림을 기다리는 중입니다. 현재 날짜로 설정되어 있으므로 알림이 거의 즉시 전송됩니다.
  • 알림을 삭제하십시오.
  • 알림을 삭제합니다 (알림을 삭제하지 않고).
  • 현재 날짜에 대한 다른 미리 알림을 만듭니다.
  • 알림을 기다립니다.
  • 알림을 클릭하여 이동합니다. 오류 가 재생됩니다.
+0

고객님의 답변을 수락하십시오. 답이없는 질문 목록에서 질문을 제거합니다. –

답변

0

내가 게시 한 실패를 재현하는 단계에 관해서는 통지를 삭제할 때 보류중인 의도가 아직 살아있는 것처럼 보이므로 다른 미리 알림을 만들 때 시스템이 이전 보류 중을 사용합니다 그 내부에있는 오래된 의도와 의도가 일치하며, 다시 이전 식별자를가집니다.

문제는 PendingIntent.getActivity() (requestCode) 메소드의 두 번째 매개 변수에 다른 숫자를 전달하여 보류중인 모든 인 텐트가 다르기 때문에 해결되는 것으로 보입니다. 시스템은 항상 시도하지 않고 새로운 보류 인 텐트를 만듭니다. 이전 것을 사용하십시오. 요청 코드가 가장 좋은 방법은 미리 알림의 데이터베이스 식별자를 사용하는 것이므로 각 미리 알림은 자체 보류 의도로 알림을받습니다.

0

답변은 정상입니다. 다른 방법으로 당신은 또한 당신의 코드가 FLAG_ONE_SHOT 대신 FLAG_UPDATE_CURRENT을 사용하여 변경할 수 있습니다 :

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

이 기존 PendingIntent에서 "엑스트라"를 덮어 쓰게됩니다.

+0

문제는 필자의 경우 상태 표시 줄에 독립된 데이터가있는 여러 알림을 한 번에 가질 수 있다는 것입니다. 그리고 각자는 다른 알림의 의도를 보완하지 않고 자신 만의 다양한 추가 기능을 갖추어야합니다. – user3289695

+1

동시에 여러 개의 통지가 필요한 경우이 솔루션을 사용할 수 없습니다. 매번 고유 한'PendingIntent'를 생성 할 필요가 있습니다. 여러분 자신의 대답에서 지정한 것처럼 고유 한'requestCode'를 사용하여 할 수 있습니다. –