致命异常:android.app.RemoteServiceException

时间:2019-05-21 04:25:49

标签: java android service

我遇到了以下提及错误

Fatal Exception: android.app.RemoteServiceException
Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{c4f1a3e u0 com.sparkle.drinkwater/com.drinkwater.DemoNoti.SleepReminderService}

这是SleepReminderService类

public class SleepReminderService extends Service {

    private Alarmio alarmio;
    private PowerManager powerManager;
    private ScreenReceiver receiver;

    @Override
    public void onCreate() {
        super.onCreate();
        alarmio = (Alarmio) getApplicationContext();
        powerManager = (PowerManager) getSystemService(POWER_SERVICE);
        receiver = new ScreenReceiver(this);
        refreshState();

        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_SCREEN_ON);
        registerReceiver(receiver, filter);
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(receiver);
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        refreshState();
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * Refresh the state of the sleepy stuff. This will either show a notification if a notification
     * should be shown, or stop the service if it shouldn't.
     */
    public void refreshState() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? powerManager.isInteractive() : powerManager.isScreenOn()) {
            AlarmData nextAlarm = getSleepyAlarm(alarmio);
            if (nextAlarm != null) {
                NotificationCompat.Builder builder;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                    if (manager != null)
                        manager.createNotificationChannel(new NotificationChannel("sleepReminder", getString(R.string.title_sleep_reminder), NotificationManager.IMPORTANCE_DEFAULT));

                    builder = new NotificationCompat.Builder(this, "sleepReminder");
                } else builder = new NotificationCompat.Builder(this);

                startForeground(540, builder.setContentTitle(getString(R.string.title_sleep_reminder))
                        .setContentText(String.format(getString(R.string.msg_sleep_reminder),
                                FormatUtils.formatUnit(this, (int) TimeUnit.MILLISECONDS.toMinutes(nextAlarm.getNext().getTimeInMillis() - System.currentTimeMillis()))))
                        .setSmallIcon(R.drawable.ic_notification_sleep)
                        .setPriority(NotificationCompat.PRIORITY_LOW)
                        .build());
                return;
            }
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
            stopSelf();

        stopForeground(true);
    }

    /**
     * Get a sleepy alarm. Well, get the next alarm that should trigger a sleep alert.
     *
     * @param alarmio       The active Application instance.
     * @return              The next [AlarmData](../data/AlarmData) that should trigger a
     *                      sleep alert, or null if there isn't one.
     */
    @Nullable
    public static AlarmData getSleepyAlarm(Alarmio alarmio) {
        if (PreferenceData.SLEEP_REMINDER.getValue(alarmio)) {
            AlarmData nextAlarm = getNextWakeAlarm(alarmio);
            if (nextAlarm != null) {
                Calendar nextTrigger = nextAlarm.getNext();
                nextTrigger.set(Calendar.MINUTE, nextTrigger.get(Calendar.MINUTE) - (int) TimeUnit.MILLISECONDS.toMinutes((long) PreferenceData.SLEEP_REMINDER_TIME.getValue(alarmio)));

                if (Calendar.getInstance().after(nextTrigger))
                    return nextAlarm;
            }
        }

        return null;
    }

    /**
     * Get the next scheduled [AlarmData](../data/AlarmData) that will ring.
     *
     * @param alarmio       The active Application instance.
     * @return              The next AlarmData that will wake the user up.
     */
    @Nullable
    public static AlarmData getNextWakeAlarm(Alarmio alarmio) {
        Calendar nextNoon = Calendar.getInstance();
        nextNoon.set(Calendar.HOUR_OF_DAY, 12);
        if (nextNoon.before(Calendar.getInstance()))
            nextNoon.set(Calendar.DAY_OF_YEAR, nextNoon.get(Calendar.DAY_OF_YEAR) + 1);
        else return null;

        Calendar nextDay = Calendar.getInstance();
        nextDay.set(Calendar.HOUR_OF_DAY, 0);
        while (nextDay.before(Calendar.getInstance()))
            nextDay.set(Calendar.DAY_OF_YEAR, nextDay.get(Calendar.DAY_OF_YEAR) + 1);

        List<AlarmData> alarms = alarmio.getAlarms();
        AlarmData nextAlarm = null;
        for (AlarmData alarm : alarms) {
            Calendar next = alarm.getNext();
            if (alarm.isEnabled && next.before(nextNoon) && next.after(nextDay) && (nextAlarm == null || nextAlarm.getNext().after(next)))
                nextAlarm = alarm;
        }

        return nextAlarm;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * To be called whenever an alarm is changed, might change, or when time might have
     * unexpectedly leaped forwards. This will start the service if there is a
     * [sleepy alarm](#getsleepyalarm) present.
     *
     * @param context       An active context instance.
     */
    public static void refreshSleepTime(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && ContextCompat.checkSelfPermission(context, Manifest.permission.FOREGROUND_SERVICE) != PackageManager.PERMISSION_GRANTED)
            return;

        Alarmio alarmio;
        if (context instanceof Alarmio)
            alarmio = (Alarmio) context;
        else alarmio = (Alarmio) context.getApplicationContext();

        if (getSleepyAlarm(alarmio) != null) {
            Intent intent = new Intent(alarmio, SleepReminderService.class);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                alarmio.startForegroundService(intent);
            else alarmio.startService(intent);
        }
    }

    private static class ScreenReceiver extends BroadcastReceiver {

        private WeakReference<SleepReminderService> serviceReference;

        public ScreenReceiver(SleepReminderService service) {
            serviceReference = new WeakReference<>(service);
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            SleepReminderService service = serviceReference.get();
            if (service != null)
                service.refreshState();
        }
    }

}

我正在我的android应用程序上使用服务。我在Fabric crashlytics上收到此错误,也无法重现此问题。这是AlarmData类

公共类AlarmData实现了Parcelable {

private int id;
public String name;
public Calendar time;
public boolean isEnabled = true;
public boolean[] isLast = new boolean[7];
public boolean[] days = new boolean[7];


public AlarmData(int id, Calendar time) {
    this.id = id;
    this.time = time;
}

public AlarmData(int id, Context context) {
    this.id = id;
    name = PreferenceData.ALARM_NAME.getSpecificOverriddenValue(context, getName(context), id);
    time = Calendar.getInstance();
    time.setTimeInMillis((long) PreferenceData.ALARM_TIME.getSpecificValue(context, id));
    isEnabled = PreferenceData.ALARM_ENABLED.getSpecificValue(context, id);
    for (int i = 0; i < 7; i++) {
        days[i] = PreferenceData.ALARM_DAY_ENABLED.getSpecificValue(context, id, i);
        isLast[i] = PreferenceData.ALARM_LAST_ENABLED.getSpecificValue(context, id, i);
    }

}

public void setDays(Context context, boolean[] days,boolean[] isLast) {
    this.days = days;
    this.isLast = isLast;

    for (int i = 0; i < 7; i++) {
        PreferenceData.ALARM_DAY_ENABLED.setValue(context, days[i], id, i);
        PreferenceData.ALARM_LAST_ENABLED.setValue(context, isLast[i], id, i);
    }
}

/**
 * Moves this AlarmData's preferences to another "id".
 *
 * @param id            The new id to be assigned
 * @param context       An active context instance.
 */
public void onIdChanged(int id, Context context) {
    PreferenceData.ALARM_NAME.setValue(context, getName(context), id);
    PreferenceData.ALARM_TIME.setValue(context, time != null ? time.getTimeInMillis() : null, id);
    PreferenceData.ALARM_ENABLED.setValue(context, isEnabled, id);
    for (int i = 0; i < 7; i++) {
        PreferenceData.ALARM_DAY_ENABLED.setValue(context, days[i], id, i);
    }
    onRemoved(context);
    this.id = id;
    if (isEnabled)
        set(context, (AlarmManager) context.getSystemService(Context.ALARM_SERVICE));
}
public boolean isRepeat() {
    for (boolean day : days) {
        if (day)
            return true;
    }

    return false;
}

public boolean isLastTrue() {
    for (boolean day : isLast) {
        if (day)
            return true;
    }

    return false;
}


/**
 * Removes this AlarmData's preferences.
 *
 * @param context       An active context instance.
 */
public void onRemoved(Context context) {
    cancel(context, (AlarmManager) context.getSystemService(Context.ALARM_SERVICE));

    PreferenceData.ALARM_NAME.setValue(context, null, id);
    PreferenceData.ALARM_TIME.setValue(context, null, id);
    PreferenceData.ALARM_ENABLED.setValue(context, null, id);
}

/**
 * Returns the user-defined "name" of the alarm, defaulting to
 * "Alarm (1..)" if unset.
 *
 * @param context       An active context instance.
 * @return              The alarm name, as a string.
 */
public String getName(Context context) {
    if (name != null)
        return name;
    else return context.getString(R.string.title_alarm, id + 1);
}



/**
 * Sets the user-defined "name" of the alarm.
 *
 * @param context       An active context instance.
 * @param name          The new name to be set.
 */
public void setName(Context context, String name) {
    this.name = name;
    PreferenceData.ALARM_NAME.setValue(context, name, id);
}

/**
 * Change the scheduled alarm time,
 *
 * @param context       An active context instance.
 * @param manager       An AlarmManager to schedule the alarm on.
 * @param timeMillis    The UNIX time (in milliseconds) that the alarm should ring at.
 *                      This is independent to days; if the time correlates to 9:30 on
 *                      a Tuesday when the alarm should only repeat on Wednesdays and
 *                      Thursdays, then the alarm will next ring at 9:30 on Wednesday.
 */
public void setTime(Context context, AlarmManager manager, long timeMillis) {
    time.setTimeInMillis(timeMillis);
    PreferenceData.ALARM_TIME.setValue(context, timeMillis, id);
    if (isEnabled)
        set(context, manager);
}

/**
 * Set whether the alarm is enabled.
 *
 * @param context       An active context instance.
 * @param manager       An AlarmManager to schedule the alarm on.
 * @param isEnabled     Whether the alarm is enabled.
 */
public void setEnabled(Context context, AlarmManager manager, boolean isEnabled) {
    this.isEnabled = isEnabled;
    PreferenceData.ALARM_ENABLED.setValue(context, isEnabled, id);
    if (isEnabled)
        set(context, manager);
    else cancel(context, manager);
}





@Nullable
public Calendar getNext() {
    if (isEnabled) {
        Calendar now = Calendar.getInstance();
        Calendar next = Calendar.getInstance();
        next.set(Calendar.HOUR_OF_DAY, time.get(Calendar.HOUR_OF_DAY));
        next.set(Calendar.MINUTE, time.get(Calendar.MINUTE));
        next.set(Calendar.SECOND, 0);

        while (now.after(next))
            next.add(Calendar.DATE, 1);

        if (isRepeat()) {
            int nextDay = next.get(Calendar.DAY_OF_WEEK) - 1; // index on 0-6, rather than the 1-7 returned by Calendar

            for (int i = 0; i < 7 && !days[nextDay]; i++) {
                nextDay++;
                nextDay %= 7;
            }

            next.set(Calendar.DAY_OF_WEEK, nextDay + 1); // + 1 = back to 1-7 range

            while (now.after(next))
                next.add(Calendar.DATE, 7);
        }

        return next;
    }

    return null;
}

/**
 * Set the next time for the alarm to ring.
 *
 * @param context       An active context instance.
 * @param manager       The AlarmManager to schedule the alarm on.
 * @return              The next [Date](https://developer.android.com/reference/java/util/Date)
 *                      at which the alarm will ring.
 */
public Date set(Context context, AlarmManager manager) {
    Calendar nextTime = getNext();
    setAlarm(context, manager, nextTime.getTimeInMillis());
    return nextTime.getTime();
}

/**
 * Schedule a time for the alarm to ring at.
 *
 * @param context       An active context instance.
 * @param manager       The AlarmManager to schedule the alarm on.
 * @param timeMillis    A UNIX timestamp specifying the next time for the alarm to ring.
 */
private void setAlarm(Context context, AlarmManager manager, long timeMillis) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        manager.setAlarmClock(
                new AlarmManager.AlarmClockInfo(
                        timeMillis,
                        PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0)
                ),
                getIntent(context)
        );
    } else {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
            manager.setExact(AlarmManager.RTC_WAKEUP, timeMillis, getIntent(context));
        else
            manager.set(AlarmManager.RTC_WAKEUP, timeMillis, getIntent(context));

        Intent intent = new Intent("android.intent.action.ALARM_CHANGED");
        intent.putExtra("alarmSet", true);
        context.sendBroadcast(intent);
    }

    manager.set(AlarmManager.RTC_WAKEUP,
            timeMillis - (long) PreferenceData.SLEEP_REMINDER_TIME.getValue(context),
            PendingIntent.getService(context, 0, new Intent(context, SleepReminderService.class), 0));

    SleepReminderService.refreshSleepTime(context);
}

/**
 * Cancel the next time for the alarm to ring.
 *
 * @param context       An active context instance.
 * @param manager       The AlarmManager that the alarm was scheduled on.
 */
public void cancel(Context context, AlarmManager manager) {
    manager.cancel(getIntent(context));

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        Intent intent = new Intent("android.intent.action.ALARM_CHANGED");
        intent.putExtra("alarmSet", false);
        context.sendBroadcast(intent);
    }
}

/**
 * The intent to fire when the alarm should ring.
 *
 * @param context       An active context instance.
 * @return              A PendingIntent that will open the alert screen.
 */
private PendingIntent getIntent(Context context) {
    Intent intent = new Intent(context, AlarmReceiver.class);
    intent.putExtra(AlarmReceiver.EXTRA_ALARM_ID, id);
    return PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}

protected AlarmData(Parcel in) {
    id = in.readInt();
    name = in.readString();
    time = Calendar.getInstance();
    time.setTimeInMillis(in.readLong());
    isEnabled = in.readByte() != 0;
    isLast = in.createBooleanArray();
    days = in.createBooleanArray();

}

@Override
public void writeToParcel(Parcel parcel, int flags) {
    parcel.writeInt(id);
    parcel.writeString(name);
    parcel.writeLong(time.getTimeInMillis());
    parcel.writeBooleanArray(days);
    parcel.writeBooleanArray(isLast);
    parcel.writeByte((byte) (isEnabled ? 1 : 0));
}

@Override
public int describeContents() {
    return 0;
}

public static final Creator<AlarmData> CREATOR = new Creator<AlarmData>() {
    @Override
    public AlarmData createFromParcel(Parcel in) {
        return new AlarmData(in);
    }

    @Override
    public AlarmData[] newArray(int size) {
        return new AlarmData[size];
    }
};

}

有人帮助我如何解决这个问题?

0 个答案:

没有答案