Android PendingEvent.getActivity()处的NullPointerException

Android PendingEvent.getActivity()处的NullPointerException,android,android-studio,android-alarms,Android,Android Studio,Android Alarms,我正在开发一款具有提醒功能的应用程序。提醒保存在sqlite中,并且在使用BootReceiver再次创建设备重启提醒时保存 在某些设备中发生NullPointerException 根据crashlytics报告,应用程序100%处于后台 以下是crashlytics日志: Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.content.Inte

我正在开发一款具有提醒功能的应用程序。提醒保存在sqlite中,并且在使用BootReceiver再次创建设备重启提醒时保存

在某些设备中发生NullPointerException

根据crashlytics报告,应用程序100%处于后台

以下是crashlytics日志:

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.content.Intent.migrateExtraStreamToClipData()' on a null object reference
       at android.app.PendingIntent.getActivity(PendingIntent.java:345)
       at android.app.PendingIntent.getActivity(PendingIntent.java:308)
       at test.reminder.services.AlarmReceiver.launchAlarmLandingPage(AlarmReceiver.java:231)
       at test.reminder.services.AlarmReceiver.access$000(AlarmReceiver.java:33)
       at test.reminder.services.AlarmReceiver$ScheduleAlarm.schedule(AlarmReceiver.java:279)
       at test.reminder.services.AlarmReceiver.setReminderAlarm(AlarmReceiver.java:122)
       at test.reminder.services.AlarmReceiver.setReminderAlarms(AlarmReceiver.java:127)
       at test.reminder.services.BootReceiver.lambda$onReceive$0(BootReceiver.java:23)
       at test.reminder.services.-$$Lambda$BootReceiver$MU2rWs8I8r27tAltl1VUIV_8WwI.run(-.java:2)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
       at java.lang.Thread.run(Thread.java:764)
以下是相关代码:

public final class AlarmReceiver extends BroadcastReceiver {

    private static final String TAG = AlarmReceiver.class.getSimpleName();
    private static final String CHANNEL_ID = "alarm_channel";

    private static final String BUNDLE_EXTRA = "bundle_extra";
    private static final String ALARM_KEY = "alarm_key";

    @Override
    public void onReceive(Context context, Intent intent) {

        final Alarm alarm = intent.getBundleExtra(BUNDLE_EXTRA).getParcelable(ALARM_KEY);
        if (alarm == null) {
            Log.e(TAG, "Alarm is null", new NullPointerException());
            return;
        }

        final int id = alarm.notificationId();

        final NotificationManager manager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        createNotificationChannel(context);

        String content = "";
        String title = "";
        if (alarm.getLabel().equals(ReminderLabelSelectionBottomSheetDialog.LABEL_BREATH_EXERCISE)) {
            content = context.getString(R.string.breath_exercise_reminder_content_message);
            title = context.getString(R.string.hello);
        } else if (alarm.getLabel().equals(ReminderLabelSelectionBottomSheetDialog.LABEL_AFFIRMATION)) {
            content = context.getString(R.string.affirmation_reminder_content_message);
            title = context.getString(R.string.hello);
        } else if (alarm.getLabel().equals(ReminderLabelSelectionBottomSheetDialog.LABEL_MEDITATION)) {
            content = context.getString(R.string.meditation_reminder_content_message);
            title = context.getString(R.string.hello);
        } else if (alarm.getLabel().equals(ReminderLabelSelectionBottomSheetDialog.LABEL_MINDFUL_PRACTICES)) {
            content = context.getString(R.string.practice_reminder_content_message);
            title = context.getString(R.string.hello);
        } else if (alarm.getLabel().equals(ReminderLabelSelectionBottomSheetDialog.LABEL_SLEEP)) {
            content = context.getString(R.string.sleep_reminder_content_message);
            title = context.getString(R.string.sleep_time);
        }

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID);
        builder.setSmallIcon(R.drawable.ic_logo_notification);
        builder.setColor(ContextCompat.getColor(context, R.color.white));
        builder.setContentTitle(title);
        builder.setStyle(new NotificationCompat.BigTextStyle().bigText(content));
        builder.setContentText(content);
        builder.setTicker(content);
        builder.setDefaults(Notification.DEFAULT_SOUND);
        builder.setDefaults(Notification.DEFAULT_LIGHTS);
        builder.setContentIntent(launchAlarmLandingPage(context, alarm));
        builder.setAutoCancel(true);
        builder.setPriority(Notification.PRIORITY_HIGH);

        if (manager != null && alarm.isEnabled()) {
            manager.notify(id, builder.build());
        }

        //Reset Alarm manually
        setReminderAlarm(context, alarm);
    }

    //Convenience method for setting a notification
    public static void setReminderAlarm(Context context, Alarm alarm) {
        Log.d("Alarming", "set reminder");

        if (!alarm.isEnabled()) {
            Log.d("Alarming", "set reminder - alarm is not enabled");
            cancelReminderAlarm(context, alarm);
            return;
        }

        final Calendar nextAlarmTime = getTimeForNextAlarm(alarm);
        alarm.setTime(nextAlarmTime.getTimeInMillis());

        final Intent intent = new Intent(context, AlarmReceiver.class);
        final Bundle bundle = new Bundle();
        bundle.putParcelable(ALARM_KEY, alarm);
        intent.putExtra(BUNDLE_EXTRA, bundle);

        final PendingIntent pIntent = PendingIntent.getBroadcast(
                context,
                alarm.notificationId(),
                intent,
                FLAG_UPDATE_CURRENT
        );

        ScheduleAlarm.with(context).schedule(alarm, pIntent);
    }

    public static void setReminderAlarms(Context context, List<Alarm> alarms) {
        for (Alarm alarm : alarms) {
            setReminderAlarm(context, alarm);
        }
    }

    /**
     * Calculates the actual time of the next alarm/notification based on the user-set time the
     * alarm should sound each day, the days the alarm is set to run, and the current time.
     *
     * @param alarm Alarm containing the daily time the alarm is set to run and days the alarm
     *              should run
     * @return A Calendar with the actual time of the next alarm.
     */
    private static Calendar getTimeForNextAlarm(Alarm alarm) {

        final Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(alarm.getTime());

        final long currentTime = System.currentTimeMillis();
        final int startIndex = getStartIndexFromTime(calendar);

        int count = 0;
        boolean isAlarmSetForDay;

        final SparseBooleanArray daysArray = alarm.getDays();

        do {
            final int index = (startIndex + count) % 7;
            isAlarmSetForDay =
                    daysArray.valueAt(index) && (calendar.getTimeInMillis() > currentTime);
            if (!isAlarmSetForDay) {
                calendar.add(Calendar.DAY_OF_MONTH, 1);
                count++;
            }
        } while (!isAlarmSetForDay && count < 7);

        return calendar;

    }

    public static void cancelReminderAlarm(Context context, Alarm alarm) {
        Log.d("Alarming", "cancel reminder alarm");

        final Intent intent = new Intent(context, AlarmReceiver.class);
        final PendingIntent pIntent = PendingIntent.getBroadcast(
                context,
                alarm.notificationId(),
                intent,
                FLAG_UPDATE_CURRENT
        );

        final AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        if (manager != null) {
            manager.cancel(pIntent);
        }
    }

    private static int getStartIndexFromTime(Calendar c) {

        final int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);

        int startIndex = 0;
        switch (dayOfWeek) {
            case Calendar.MONDAY:
                startIndex = 0;
                break;
            case Calendar.TUESDAY:
                startIndex = 1;
                break;
            case Calendar.WEDNESDAY:
                startIndex = 2;
                break;
            case Calendar.THURSDAY:
                startIndex = 3;
                break;
            case Calendar.FRIDAY:
                startIndex = 4;
                break;
            case Calendar.SATURDAY:
                startIndex = 5;
                break;
            case Calendar.SUNDAY:
                startIndex = 6;
                break;
        }

        return startIndex;

    }

    private static void createNotificationChannel(Context ctx) {
        if (SDK_INT < O) return;

        final NotificationManager mgr = ctx.getSystemService(NotificationManager.class);
        if (mgr == null) return;

        final String name = ctx.getString(R.string.channel_name);
        if (mgr.getNotificationChannel(name) == null) {
            final NotificationChannel channel =
                    new NotificationChannel(CHANNEL_ID, name, IMPORTANCE_HIGH);
            mgr.createNotificationChannel(channel);
        }
    }

    private static PendingIntent launchAlarmLandingPage(Context ctx, Alarm alarm) {
        return PendingIntent.getActivity(
                ctx, alarm.notificationId(), launchIntent(ctx, alarm.getLabel()), FLAG_UPDATE_CURRENT
        );
    }

    public static Intent launchIntent(Context context, String type) {
        //Normalde labela göre farklı activityler başlatmamız gerek
        // ancak biz çoğu sayfa için fragment kullandığımız için yalnızca MainActivitye yönlendiriyoruz
        final Intent i = new Intent(context, SplashScreenActivity.class);
        i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        return i;
    }

    private static class ScheduleAlarm {

        @NonNull
        private final Context ctx;
        @NonNull
        private final AlarmManager am;

        private ScheduleAlarm(@NonNull AlarmManager am, @NonNull Context ctx) {
            this.am = am;
            this.ctx = ctx;
        }

        static ScheduleAlarm with(Context context) {
            final AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
            if (am == null) {
                throw new IllegalStateException("AlarmManager is null");
            }
            return new ScheduleAlarm(am, context);
        }

        void schedule(Alarm alarm, PendingIntent pi) {
            if (SDK_INT > LOLLIPOP) {
                am.setAlarmClock(new AlarmManager.AlarmClockInfo(alarm.getTime(), launchAlarmLandingPage(ctx, alarm)), pi);
            } else {
                am.setExact(AlarmManager.RTC_WAKEUP, alarm.getTime(), pi);
            }
        }

    }

}
public class BootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
            Executors.newSingleThreadExecutor().execute(() -> {
                final List<Alarm> alarms = DatabaseHelper.getInstance(context).getAlarms();
                setReminderAlarms(context, alarms);
            });
        }
    }

}
公共最终类AlarmReceiver扩展了BroadcastReceiver{
私有静态最终字符串标记=AlarmReceiver.class.getSimpleName();
专用静态最终字符串通道\u ID=“报警\u通道”;
私有静态最终字符串BUNDLE\u EXTRA=“BUNDLE\u EXTRA”;
专用静态最终字符串ALARM\u KEY=“ALARM\u KEY”;
@凌驾
公共void onReceive(上下文、意图){
最终报警=intent.getBundleExtra(BUNDLE\u EXTRA).getParcelable(报警键);
如果(报警==null){
Log.e(标记“报警为空”,新的NullPointerException());
返回;
}
final int id=alarm.notificationId();
最终通知经理=
(NotificationManager)context.getSystemService(context.NOTIFICATION\u服务);
createNotificationChannel(上下文);
字符串内容=”;
字符串标题=”;
if(alarm.getLabel().equals(提醒标签选择底部表单对话框.标签\u呼吸\u练习)){
content=context.getString(R.string.breath\u练习\u提醒\u内容\u消息);
title=context.getString(R.string.hello);
}else if(alarm.getLabel().equals(提醒标签选择底部表单对话框.标签\确认)){
content=context.getString(R.string.assemption\u-rements\u-content\u-message);
title=context.getString(R.string.hello);
}else if(alarm.getLabel().equals(提醒LabelSelectionBottomSheetDialog.LABEL_)){
content=context.getString(R.string.冥想\u提醒\u内容\u消息);
title=context.getString(R.string.hello);
}else if(alarm.getLabel().equals(提醒标记选择BottomSheetDialog.LABEL\u实践)){
content=context.getString(R.string.practice\u-rements\u-content\u-message);
title=context.getString(R.string.hello);
}else if(alarm.getLabel().equals(提醒LabelSelectBottomSheetDialog.LABEL_SLEEP)){
content=context.getString(R.string.sleep\u rements\u content\u message);
title=context.getString(R.string.sleep\u time);
}
NotificationCompat.Builder=新建NotificationCompat.Builder(上下文,通道ID);
生成器.设置图标(R.可绘制.ic_徽标_通知);
setColor(ContextCompat.getColor(context,R.color.white));
builder.setContentTitle(标题);
builder.setStyle(新NotificationCompat.BigTextStyle().bigText(内容));
builder.setContentText(内容);
builder.setTicker(内容);
builder.setDefaults(通知.DEFAULT\u声音);
builder.setDefaults(Notification.DEFAULT_LIGHTS);
setContentIntent(启动AlarmLandingPage(上下文,报警));
builder.setAutoCancel(true);
builder.setPriority(通知优先级高);
if(manager!=null&&alarm.isEnabled()){
manager.notify(id,builder.build());
}
//手动重置报警
设置警报(上下文、警报);
}
//设置通知的简便方法
公共静态无效设置提醒器(上下文上下文、报警){
日志d(“报警”、“设置提醒”);
如果(!alarm.isEnabled()){
日志d(“报警”,“设置提醒-报警未启用”);
取消报警(上下文、报警);
返回;
}
最终日历nextAlarmTime=getTimeForNextAlarm(报警);
alarm.setTime(nextAlarmTime.getTimeInMillis());
最终意图=新意图(上下文,AlarmReceiver.class);
最终捆绑=新捆绑();
捆绑。可打包(报警键、报警);
意向。额外支付(捆绑额外支付,捆绑);
最终PendingEvent pIntent=PendingEvent.getBroadcast(
上下文
alarm.notificationId(),
意图
标志\u更新\u当前
);
ScheduleAlarm.with(context).schedule(alarm,pIntent);
}
公共静态无效设置(上下文上下文、列表报警){
用于(报警:报警){
设置警报(上下文、警报);
}
}
/**
*根据用户设置的时间计算下一次报警/通知的实际时间
*警报应每天响起,警报设置为运行的日期和当前时间。
*
*@param alarm alarm包含报警设置为运行的每日时间和报警天数
*应该运行
*@返回带有下一次报警实际时间的日历。
*/
私有静态日历getTimeForNextAlarm(报警){
最终日历=Calendar.getInstance();
calendar.setTimeInMillis(alarm.getTime());
最终长currentTime=System.currentTimeMillis();
final int startIndex=getStartIndexFromTime(日历);
整数计数=0;
布尔isAlarmSetForDay;
最终SparseBooleanArray daysArray=alarm.getDays();
做{
最终整数指数=(startIndex+计数)%7;
伊萨拉姆斯特福德=
daysArray.valueAt(index)和&(calendar.getTimeInMillis()>currentTime);
如果(!isAlarmSetForDay){
calendar.add(calendar.DAY/u/u月,1);
计数++;
}
}而(!isAlarmSetForDay&&count<7);
返回日历;
}
公共静态无效取消提醒(上下文上下文、报警){
日志d(“报警”、“取消提醒报警”);
最终意图=新意图(上下文,AlarmReceiver.class);

public final class Alarm implements Parcelable {

    private Alarm(Parcel in) {
        id = in.readLong();
        time = in.readLong();
        label = in.readString();
        allDays = in.readSparseBooleanArray();
        isEnabled = in.readByte() != 0;
    }

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

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

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

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeLong(id);
        parcel.writeLong(time);
        parcel.writeString(label);
        parcel.writeSparseBooleanArray(allDays);
        parcel.writeByte((byte) (isEnabled ? 1 : 0));
    }

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({MON, TUES, WED, THURS, FRI, SAT, SUN})
    @interface Days {
    }

    public static final int MON = 1;
    public static final int TUES = 2;
    public static final int WED = 3;
    public static final int THURS = 4;
    public static final int FRI = 5;
    public static final int SAT = 6;
    public static final int SUN = 7;

    private static final long NO_ID = -1;

    private final long id;
    private long time;
    private String label;
    private SparseBooleanArray allDays;
    private boolean isEnabled;

    public Alarm() {
        this(NO_ID);
    }

    public Alarm(long id) {
        this(id, System.currentTimeMillis());
    }

    public Alarm(long id, long time, @Days int... days) {
        this(id, time, null, days);
    }

    public Alarm(long id, long time, String label, @Days int... days) {
        this.id = id;
        this.time = time;
        this.label = label;
        this.allDays = buildDaysArray(days);
    }

    public long getId() {
        return id;
    }

    public void setTime(long time) {
        this.time = time;
    }

    public long getTime() {
        return time;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getLabel() {
        return label;
    }

    public void setDay(@Days int day, boolean isAlarmed) {
        allDays.append(day, isAlarmed);
    }

    public SparseBooleanArray getDays() {
        return allDays;
    }

    public boolean getDay(@Days int day) {
        return allDays.get(day);
    }

    public void setIsEnabled(boolean isEnabled) {
        this.isEnabled = isEnabled;
    }

    public boolean isEnabled() {
        return isEnabled;
    }

    public int notificationId() {
        final long id = getId();
        return (int) (id ^ (id >>> 32));
    }

    @Override
    public String toString() {
        return "Alarm{" +
                "id=" + id +
                ", time=" + time +
                ", label='" + label + '\'' +
                ", allDays=" + allDays +
                ", isEnabled=" + isEnabled +
                '}';
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + (int) (id ^ (id >>> 32));
        result = 31 * result + (int) (time ^ (time >>> 32));
        result = 31 * result + label.hashCode();
        for (int i = 0; i < allDays.size(); i++) {
            result = 31 * result + (allDays.valueAt(i) ? 1 : 0);
        }
        return result;
    }

    private static SparseBooleanArray buildDaysArray(@Days int... days) {

        final SparseBooleanArray array = buildBaseDaysArray();

        for (@Days int day : days) {
            array.append(day, true);
        }

        return array;

    }

    private static SparseBooleanArray buildBaseDaysArray() {

        final int numDays = 7;

        final SparseBooleanArray array = new SparseBooleanArray(numDays);

        array.put(MON, false);
        array.put(TUES, false);
        array.put(WED, false);
        array.put(THURS, false);
        array.put(FRI, false);
        array.put(SAT, false);
        array.put(SUN, false);

        return array;

    }

}