如何防止我的三星设备阻止我的警报应用程序工作?

时间:2018-03-15 07:38:18

标签: android alarmmanager

Android 6,7和8在日历提供程序中有一个错误,它使警报无法随机工作: https://issuetracker.google.com/issues/64502046

所以我开发了自己的基于日历的闹钟应用程序。但是在我使用Android 7的三星Galaxy J5 2017中它无法正常工作。几乎我在其上设置的每个闹钟都能正常工作,但是有一个闹钟无效,我不知道为什么。

我怀疑三星的电源管理设置,所以我在电池设置中故意让应用程序进入睡眠状态,并设置一些警报以查看电源设置是否导致问题。但我测试的所有警报都有效。所以我无法重现失败。我无法弄清楚为什么有一个不起作用的警报。它设置得当,因为我读了应用程序的日志,但是当时间到了,警报接收器没有被调用,我无法重现它。我知道它没有被调用,因为在代码中我做的第一件事就是记录消息并且消息不存在于日志中。此外,在无法启动的警报之前,设备未重新启动且未卸载应用程序,因此警报仍必须设置。

这是设置闹钟的类:

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 {
        Intent i = new Intent(mContext, OnAlarmReceiver.class);
        i.putExtra(RemindersDbAdapter.KEY_ROWID, taskId);
        i.putExtra(ReminderManager.EXTRA_ALARM_ID, alarmId);
        PendingIntent pi = getReminderPendingIntent(i, taskId);

        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 date    date for logging purposes.
     */
    public void unsetReminder(long taskId, String date) {
        Intent i = new Intent(mContext, OnAlarmReceiver.class);
        PendingIntent pi = getReminderPendingIntent(i, taskId);
        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 i the intent to be used.
     * @param taskId reminder database identifier, it distingishes each alarm from the others.
     * @return the <code>PendingIntent</code> object.
     */
    private PendingIntent getReminderPendingIntent(Intent i, long taskId) {
        PendingIntent pi = PendingIntent.getBroadcast(mContext, (int)taskId, i, PendingIntent.FLAG_UPDATE_CURRENT);
        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);
        }
    }
}

这是每个闹钟响起时应该调用的类:

package bembibre.alarmfix.alarms;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

import bembibre.alarmfix.alarms.intentservices.ReminderService;
import bembibre.alarmfix.database.RemindersDbAdapter;
import bembibre.alarmfix.logging.Logger;

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

/**
 * Receives alarms from the OS.
 */
public class OnAlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Logger.log("An alarm has been received right now.");
        long rowid = intent.getExtras().getLong(RemindersDbAdapter.KEY_ROWID);
        long alarmId = intent.getExtras().getLong(ReminderManager.EXTRA_ALARM_ID);
        WakeReminderIntentService.acquireStaticLock(context);
        Intent i = new Intent(context, ReminderService.class);
        i.putExtra(RemindersDbAdapter.KEY_ROWID, rowid);
        i.putExtra(ReminderManager.EXTRA_ALARM_ID, alarmId);
        context.startService(i);
    }
}

完整代码在此处:https://github.com/maykelbembibre/androidcalendarfix

1 个答案:

答案 0 :(得分:0)

只要没有人能够找到解决问题的有效解决方案,我自己能够应用的唯一笨拙的解决方案就是由以下几部分组成:

  • 保存数据库中的每个待处理警报,并在触发后立即将其删除,用户删除,重新启动电话等等。每次打开应用程序时,请检查是否存在过去保存的警报,并警告用户它没有被触发。
  • 请记住用户应该查看其设备的电源设置,因为某些智能手机品牌创建的电源管理软件实际上是为了防止应用程序工作并消耗电量,除非用户允许它们启用。

所有这一切,我认为经过这么长时间,Google应该将应用程序的持续激活作为应用程序自动请求的权限,与其他权限相同,以便用户理解它是一个警报应用程序并允许该许可,无论电源管理设置如何,应用程序都能够使用始终可靠的警报。