Android应用ListView删除日历事件后重启恢复延迟问题求助
Android日历事件删除后重复出现的问题分析
问题现象
开发Android应用时,使用ContentResolver删除日历事件后,ListView能即时刷新显示删除效果,但重新打开ListView时,已删除的事件会再次出现,需延迟数分钟才会被永久删除。尝试使用单例ContentResolver后问题仍未解决。
相关代码
Calendar_ListView.java
package com.example.eventhub; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.drawable.AnimationDrawable; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.RelativeLayout; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.constraintlayout.widget.ConstraintLayout; import java.util.ArrayList; public class Calendar_ListView extends AppCompatActivity { public ArrayList<String> Events_Name = new ArrayList<String>(); public ArrayList<String> temporary = new ArrayList<String>(); ArrayAdapter arrayAdapter; Utility utility; Context context; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.reminder_list_view); context = getApplicationContext(); utility = new Utility(context); RelativeLayout relativeLayout = findViewById(R.id.ListId); AnimationDrawable animationDrawable = (AnimationDrawable) relativeLayout.getBackground(); animationDrawable.setEnterFadeDuration(3500); animationDrawable.setExitFadeDuration(5500); animationDrawable.start(); ListView listview = findViewById(R.id.listview); arrayAdapter = new ArrayAdapter(Calendar_ListView.this, R.layout.listview_custom_itemview, temporary); listview.setAdapter(arrayAdapter); listview.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { AlertDialog.Builder builder = new AlertDialog.Builder(Calendar_ListView.this); builder.setMessage("Do you want to delete this reminder?") .setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { utility.deleteEventByTitle2(context, Events_Name.get(position)); Log.d("FETCH", Events_Name.toString()); Log.d("FETCH", temporary.toString()); arrayAdapter.remove(arrayAdapter.getItem(position)); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); } }); AlertDialog dialog = builder.create(); dialog.show(); } }); } @Override protected void onResume() { super.onResume(); Fetch_EventNames(); Log.d("LISTVIEW", Events_Name.toString()); arrayAdapter.notifyDataSetChanged(); } public void Fetch_EventNames(){ utility.readCalendarEvent(context); Events_Name.clear(); temporary.clear(); ArrayList<String> titles = utility.nameOfEvent; ArrayList<String> myEvents = new ArrayList<String>(); for(String str : titles){ if(str != null && str.length() > 3 && str.substring(str.length() - 3).equals("_eh")){ myEvents.add(str); } } Events_Name = myEvents; for(String str : Events_Name){ temporary.add(str.substring(0, str.length()-3)); } } }
Utility.java
package com.example.eventhub; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.provider.CalendarContract; import android.util.Log; import android.widget.Toast; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; public class Utility { public ArrayList<String> nameOfEvent = new ArrayList<String>(); public ArrayList<ArrayList<String>> Event_Start_End = new ArrayList<ArrayList<String>>(); public ArrayList<String> startDates = new ArrayList<String>(); public ArrayList<String> endDates = new ArrayList<String>(); public ArrayList<String> descriptions = new ArrayList<String>(); public ArrayList<String> eventIds = new ArrayList<String>(); ContentResolver resolver; public Utility(Context context){ resolver = context.getContentResolver(); } public ArrayList<String> readCalendarEvent(Context context) { Cursor cursor = resolver .query( Uri.parse("content://com.android.calendar/events"), new String[]{"calendar_id", "title", "description", "dtstart", "dtend", "eventLocation"}, null, null, null); cursor.moveToFirst(); String CNames[] = new String[cursor.getCount()]; nameOfEvent.clear(); startDates.clear(); endDates.clear(); descriptions.clear(); Log.d("UTILITY_OBJ", "SIZE: "+ Integer.toString(cursor.getCount())); for (int i = 0; i < CNames.length; i++) { nameOfEvent.add(cursor.getString(1)); if(cursor.getString(3) != null){ startDates.add(getDate(Long.parseLong(cursor.getString(3)))); } if(cursor.getString(4) != null){ endDates.add(getDate(Long.parseLong(cursor.getString(4)))); } descriptions.add(cursor.getString(2)); CNames[i] = cursor.getString(1); cursor.moveToNext(); } return nameOfEvent; } public void deleteEventByTitle(Context context, String title) { Cursor cursor = resolver.query( Uri.parse("content://com.android.calendar/events"), new String[]{"_id"}, "title=?", new String[]{title}, null); if (cursor.moveToFirst()) { int temp = cursor.getColumnIndex("_id"); long id; if(temp >= 0) { id = cursor.getLong(temp); Uri deleteUri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, id); int rows = resolver.delete(deleteUri, null, null); Log.d("UTILITY_OBJ", "Rows deleted: " + rows); Toast.makeText(context, "Event Successfully Deleted", Toast.LENGTH_SHORT).show(); } else { Log.d("UTILITY_OBJ", "EVENT ID NOT FOUND"); Toast.makeText(context, "Event NOT FOUND!!", Toast.LENGTH_SHORT).show(); } } else { Log.d("UTILITY_OBJ", "EVENT ID NOT FOUND"); Toast.makeText(context, "Event NOT FOUND!!", Toast.LENGTH_SHORT).show(); } cursor.close(); } public String getDate(long milliSeconds) { SimpleDateFormat formatter = new SimpleDateFormat( "dd/MM/yyyy hh:mm:ss a"); Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(milliSeconds); return formatter.format(calendar.getTime()); } public ArrayList<String> readCalendarEvent2(Context context) { Cursor cursor = context.getContentResolver().query( Uri.parse("content://com.android.calendar/events"), new String[]{"_id", "calendar_id", "title", "description", "dtstart", "dtend", "eventLocation"}, null, null, null); cursor.moveToFirst(); String CNames[] = new String[cursor.getCount()]; nameOfEvent.clear(); startDates.clear(); endDates.clear(); descriptions.clear(); eventIds.clear(); Log.d("UTILITY_OBJ", "SIZE: "+ Integer.toString(cursor.getCount())); for (int i = 0; i < CNames.length; i++) { eventIds.add(cursor.getString(0)); nameOfEvent.add(cursor.getString(2)); if(cursor.getString(4) != null){ startDates.add(getDate(Long.parseLong(cursor.getString(4)))); } if(cursor.getString(5) != null){ endDates.add(getDate(Long.parseLong(cursor.getString(5)))); } descriptions.add(cursor.getString(3)); CNames[i] = cursor.getString(2); cursor.moveToNext(); } return nameOfEvent; } public void deleteEventByTitle2(Context context, String title) { Cursor cursor = resolver.query( Uri.parse("content://com.android.calendar/events"), new String[]{"_id"}, "title=?", new String[]{title}, null); ArrayList<String> eventsToDelete = new ArrayList<String>(); if (cursor.moveToFirst()) { int temp = cursor.getColumnIndex("_id"); long id; if(temp >= 0) { id = cursor.getLong(temp); eventsToDelete.add(title); Uri deleteUri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, id); int rows = resolver.delete(deleteUri, null, null); Log.d("UTILITY_OBJ", "Rows deleted: " + rows); Toast.makeText(context, "Event Successfully Deleted", Toast.LENGTH_SHORT).show(); } else { Log.d("UTILITY_OBJ", "EVENT ID NOT FOUND"); Toast.makeText(context, "Event NOT FOUND!!", Toast.LENGTH_SHORT).show(); } } else { Log.d("UTILITY_OBJ", "EVENT ID NOT FOUND"); Toast.makeText(context, "Event NOT FOUND!!", Toast.LENGTH_SHORT).show(); } cursor.close(); for (String event : eventsToDelete) { nameOfEvent.remove(event); } } }
可能的原因及解决方案
1. 日历系统同步延迟
Android日历会与云端账户同步,本地执行删除后,系统需要时间完成本地与云端的同步,在此期间查询会读取未更新的缓存数据。
解决方案:
删除事件后主动通知ContentResolver数据变化,触发系统更新缓存:
// 在deleteEventByTitle2方法的resolver.delete之后添加 resolver.notifyChange(CalendarContract.Events.CONTENT_URI, null);
2. ContentResolver查询缓存
ContentResolver默认会缓存查询结果,短时间内重新查询可能返回旧数据。
解决方案:
查询时添加参数禁用缓存:
Bundle queryArgs = new Bundle(); queryArgs.putBoolean(ContentResolver.QUERY_ARG_NO_CURSOR_COPY, true); Cursor cursor = resolver.query( CalendarContract.Events.CONTENT_URI, new String[]{"calendar_id", "title", "description", "dtstart", "dtend", "eventLocation"}, queryArgs, null );
3. 标题删除的不可靠性
当前通过标题匹配删除事件,若存在同名事件,仅会删除第一个匹配项,其余同名事件仍会保留,导致重新查询时再次出现。
解决方案:
优先通过事件ID删除:
- 读取事件时保存ID:在
readCalendarEvent方法中添加事件ID的存储 - 删除时直接通过ID定位,替代标题匹配逻辑
4. 硬编码日历URI问题
代码中使用硬编码的content://com.android.calendar/events,不同厂商/版本的日历URI可能存在差异,导致删除操作未作用到正确数据源。
解决方案:
使用官方提供的CalendarContract.Events.CONTENT_URI替代硬编码URI,确保兼容性。
5. 权限验证缺失
若未正确申请并获取WRITE_CALENDAR权限,删除操作可能未真正执行,仅修改了本地列表,重新查询时会从日历读取原数据。
解决方案:
在Manifest中添加权限:
<uses-permission android:name="android.permission.WRITE_CALENDAR" /> <uses-permission android:name="android.permission.READ_CALENDAR" />
并在运行时动态申请权限。
内容的提问来源于stack exchange,提问作者Pratham Koli
相关产品推荐
相关产品推荐

