You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android创建日历事件及提醒时触发SQLiteException异常求助

解决Android日历提醒插入的SQLiteException问题

看起来你遇到的是典型的跨设备兼容性问题,这种部分设备正常、部分崩溃的情况,通常和硬编码值、权限处理或者字段使用不当有关。我来帮你拆解问题并给出修复方案:

核心问题分析

你的代码里有几个容易触发跨设备异常的点:

1. 硬编码日历ID(CALENDAR_ID=1)

不同设备的默认日历ID不一定是1,有些设备的主日历可能是2或者其他值,硬编码直接插入会导致SQLite写入失败,因为目标日历不存在。

2. 时区字段使用错误

你这里写的CalendarContract.Calendars.CALENDAR_TIME_ZONE是字段名常量(字符串值是calendar_timezone),而不是实际的时区ID。插入事件时需要传入真实的时区值(比如Asia/Shanghai),否则系统无法解析时区,导致事件插入失败,后续插入提醒自然也会抛异常。

3. 权限处理不完整

虽然你声明了SecurityException捕获,但Android 6.0+需要动态申请WRITE_CALENDAR和READ_CALENDAR权限,部分设备可能没有授予权限,导致数据库写入被拒绝。

4. 事件插入后的空值判断缺失

如果事件插入失败(比如权限不足、日历ID错误),uri会是null,这时候调用uri.getLastPathSegment()会直接抛出空指针异常,进而引发后续的SQLiteException。

修复后的完整代码

package com.medikoe.connect.androidmedikoeconnect.utilities; 

import android.app.Activity; 
import android.content.ContentResolver; 
import android.content.ContentValues; 
import android.content.pm.PackageManager; 
import android.net.Uri; 
import android.provider.CalendarContract; 
import android.widget.Toast; 
import java.util.TimeZone; 

public class PushReminder { 
    private Activity activity; 
    private static final int REQUEST_CALENDAR_PERMISSIONS = 1001;

    public PushReminder(Activity activity) { 
        this.activity = activity; 
    } 

    public void addReminder(long startTime) { 
        // 先检查权限
        if (activity.checkSelfPermission(android.Manifest.permission.WRITE_CALENDAR) != PackageManager.PERMISSION_GRANTED ||
            activity.checkSelfPermission(android.Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
            activity.requestPermissions(new String[]{android.Manifest.permission.WRITE_CALENDAR, android.Manifest.permission.READ_CALENDAR}, REQUEST_CALENDAR_PERMISSIONS);
            Toast.makeText(activity, "请授予日历权限以创建提醒", Toast.LENGTH_SHORT).show();
            return;
        }

        ContentResolver cr = activity.getContentResolver(); 
        // 获取用户默认日历ID(替代硬编码的1)
        long defaultCalendarId = getDefaultCalendarId(cr);
        if (defaultCalendarId == -1) {
            Toast.makeText(activity, "未找到可用的日历", Toast.LENGTH_SHORT).show();
            return;
        }

        ContentValues calEvent = new ContentValues(); 
        calEvent.put(CalendarContract.Events.CALENDAR_ID, defaultCalendarId); 
        calEvent.put(CalendarContract.Events.TITLE, "Add Shop Reminder"); 
        calEvent.put(CalendarContract.Events.DTSTART, startTime); 
        calEvent.put(CalendarContract.Events.DTEND, startTime + 5 * 60 * 1000); // 直接计算,避免Date对象的额外开销
        calEvent.put(CalendarContract.Events.HAS_ALARM, 1); 
        calEvent.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().getID()); // 使用真实时区ID

        Uri uri = cr.insert(CalendarContract.Events.CONTENT_URI, calEvent); 
        if (uri == null) {
            Toast.makeText(activity, "创建日历事件失败", Toast.LENGTH_SHORT).show();
            return;
        }

        int eventId = Integer.parseInt(uri.getLastPathSegment()); 
        Toast.makeText(activity, "Created Calendar Event " + eventId, Toast.LENGTH_SHORT).show(); 

        ContentValues reminders = new ContentValues(); 
        reminders.put(CalendarContract.Reminders.EVENT_ID, eventId); 
        reminders.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT); 
        reminders.put(CalendarContract.Reminders.MINUTES, 10); 

        Uri reminderUri = cr.insert(CalendarContract.Reminders.CONTENT_URI, reminders); 
        if (reminderUri != null) {
            Toast.makeText(activity, "Reminder have been saved successfully", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(activity, "创建提醒失败", Toast.LENGTH_SHORT).show();
        }
    } 

    // 获取用户默认日历ID的工具方法
    private long getDefaultCalendarId(ContentResolver cr) {
        Uri calendarUri = CalendarContract.Calendars.CONTENT_URI;
        String[] projection = {CalendarContract.Calendars._ID};
        String selection = CalendarContract.Calendars.IS_PRIMARY + " = ?";
        String[] selectionArgs = {"1"};

        // 先查询主日历
        android.database.Cursor cursor = cr.query(calendarUri, projection, selection, selectionArgs, null);
        if (cursor != null && cursor.moveToFirst()) {
            long id = cursor.getLong(0);
            cursor.close();
            return id;
        }

        // 如果没有主日历,取第一个可用日历
        cursor = cr.query(calendarUri, projection, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            long id = cursor.getLong(0);
            cursor.close();
            return id;
        }

        if (cursor != null) {
            cursor.close();
        }
        return -1;
    } 
}

额外注意事项

  • 权限申请回调:记得在Activity中重写onRequestPermissionsResult方法,在用户授予权限后重新调用addReminder方法。
  • 时区兼容性:使用TimeZone.getDefault().getID()可以适配不同地区的设备,避免时区解析错误。
  • 异常捕获:可以在代码中添加try-catch块捕获SQLiteException,方便定位具体的设备兼容性问题。

内容的提问来源于stack exchange,提问作者Arpit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:38:44