我遇到了以下提及错误
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];
}
};
}
有人帮助我如何解决这个问题?