记一次向系统日历写入数据的经历

5,632 阅读7分钟

       最近接到一个向系统日历里写入事件的需求,由于之前没做过这个,猜测可能是系统数据库的增删改查操作。但是第一次听到这个需求还是有点慌的,万一各个不同厂商要分别处理怎么办,万一低版本系统不支持怎么办,所以我第一时间对这个功能进行了调研。出乎意外地顺利地是,只需要网上一搜索“android 写入日历事件”,便有了很多的结果,在此必须感谢大牛们的无私分享,也坚定了自己要多写博客的信心,所以今天把我处理日历需求的经过给大家分享一下。

系统的日历数据库是哪个,什么是日历账号,如何申请自己的日历账号?

       这里直接贴出代码中使用到的数据库uri以及日历账户,其中前三个是系统数据库的uri,这里我们一一来解释。第一个是日历数据库的总uri,必须要有这个uri才能打开系统的日历数据库。第二个是事件uri,如果想向日历中插入事件,在系统的contentResolver中传入这个key。第三个是给系统添加提醒,有了这个提醒以后,到点了就会在通知栏显示出日历通知(当然前提是app有通知栏权限)。最后的4个都是本app在系统日历中的账户信息,用来给系统作区分的,随便填填就好了

private static String CALENDER_URL = "content://com.android.calendar/calendars";
private static String CALENDER_EVENT_URL = "content://com.android.calendar/events";
private static String CALENDER_REMINDER_URL = "content://com.android.calendar/reminders";
private static String CALENDARS_NAME = "aaaa";
private static String CALENDARS_ACCOUNT_NAME = "aaaa@aaaa.com";
private static String CALENDARS_ACCOUNT_TYPE = "com.android.aaaa";
private static String CALENDARS_DISPLAY_NAME = "aaaa账户";

检查是否有日历账号,如果没有账号就添加一个


添加日历账户也是数据库操作,具体代码如下:


代码来操作系统日历需要什么权限?

在安卓生态中,做很多操作都需要权限,更别提这是操作系统日历了。读写日历权限分别是

Manifest.permission.READ_CALENDAR
Manifest.permission.WRITE_CALENDAR

首先我们要在manifest里面静态申请一下

<uses-permission android:name="android.permission.READ_CALENDAR"/>
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>

其次,为了满足高API要求,还需要在实际使用的地方动态申请日历权限,代码如下,这里面的rxjava代码暂时不看,后面会讲到


应该怎么样去写入和删除日历?       

写入和删除日历其实就是系统数据库操作,这些代码非常简单,基本复制过去就可以使用。我这里不直接贴代码是因为排版会错乱,后面我会贴上另外一个写得比较好的帖子的链接,我也是从那里参考来的,感兴趣的话可以去那边复制。如下图,设置好了日历需要的参数,然后调用系统contentResolver的insert方法进行写入到库中以及提醒中,其中event.put(CalendarContract.Events.DTSTART, start);

就是设置提醒的时间,时间是以毫秒为单位


删除也是一样,调用数据库方法即可


坑点来了,这是满满的干货

按照上面的方式半个小时就调通了写入和删除日历功能,但是一般解决问题的时间比实际功能开发的时间总是要多不少,不然就不会出现那么多的bug了。哈哈,废话少说,直接干货送上

坑点一  个别手机在插入日历的时候会crash

安卓生态就是这么奇葩,各种各样的定制系统,各种各样的系统版本,所以我们没法测试到所有手机的兼容性,正因如此,所以才会经常碰到很奇怪的问题。在插入的时候报错了,具体错误如下:


定位到这行错误的源码出,就是在写入日历时失败了,报了一个空指针。出现的概率很低,是在SIMULATOR 6.0.1的版本上出现的,这手机确实很少见,没人知道是什么情况,但是问题还是要解决一下滴,不然crash率就降不下来了。由于是系统插入失败,所以这里不好干预,我是直接做了一个try..catch处理了,反正不报bug心情就舒畅了哈哈。


坑点二 部分手机明明没有日历权限,系统给你返回有权限

这问题真的是头痛了半天,这里必须吐槽一下。出问题的是oppo r9手机上,用户关闭了日历权限以后,系统给返回有权限,但是又写不进去日历。吐槽完了,问题还是得自己来解决,谁让测试小姐姐那么可爱呢。经过我一整套流程的debug,发现只要没权限,系统给返回的cursor一定是为null的,这样就有了解决思路了,只需要在每次执行插入和删除前多加一个cursor是否为null的判断就好了,说干就干,代码如下:


这就是前面在申请权限是有那么多的CalendarReminderUtils.isNoCursor(App.getInstance())的原因,这也是对碎片化满满的无奈啊。。。


坑点三 如何判断是否添加成功

上面说到了部分手机可能在没权限的时候返回有权限,那么在添加以后就不能保证目前百分百添加成功了。如果添加失败了,就应该给一个弹框或者提示。我这里只能是在添加后再判断一下系统数据库中是否有数据,如果没有数据的话就弹无权限的框,为了兼容那少部分手机也是没办法了,必须多做很多的操作。这里如果没权限,就调用showWaringDialog方法来显示弹框提示用户要给日历权限



坑点四 需要先删除之前的提醒再添加,不支持修改

       最初的话是每调用一次就添加一个提醒,所以后面日历里都塞满了类似的提醒,感觉真的很low,而日历数据库又不支持修改,所以需要注意的是每次在新增时需要考虑是否需要删除之前的日历,不然日历里就会越来越多重复的事件。在删除后,再添加具体的新日历提醒,具体代码如下图:


坑点五 有些手机上删除不了日历事件

       这是在一台三星手机上复现的,其他国内品牌手机都没有这问题。就是调用了系统的删除日历代码,也没有报任何错误,但是去系统数据库查找的时候那条日历事件还是存在,和没删除一个样。

       这个问题是在stackoverflow上面找到了答案,说是三星手机删除日历事件是属于软删除,即将deleted这个flag置为0的方式来删除,三星也喜欢玩非大众化,不然咱们拥有强大心脏的安卓开发者早就已经习惯了。

这个解决方法是在查询时加一个条件,就是deleted != 1,因为被删除了在三星上就是0


总结:android向日历中写入事件总体来说还是比较简单的,没有想象中不同平台不同实现方式那么恐怖,网上的例子很多,拷贝过去稍微改改就能用了,有需求的可以在下面的链接中复制代码,我这里就不再赘述了。所以基本实现不太值钱,但是实际项目中碰到的坑点就很难得了,我这里已经把我碰到的坑点都列举了出来,希望能对大家有所帮助。

demo地址:github.com/dongrong-fu…