【发布时间】:2015-02-03 03:58:55
【问题描述】:
(编辑两次)
我的 sqlite 游标没有收到更改通知,即使我在创建它们时设置了通知并在访问数据库时通知它们。我尝试了这样的方法来测试方法:
weatherCursor.getCount(); // returns 1
deleteAllRecords();
Uri uri = weatherCursor.getNotificationUri();
getContext().getContentResolver().notifyChange(uri, null);
weatherCursor.getCount(); // still returns 1
weatherCursor.close();
weatherCursor = getContext().getContentResolver().query(uri, null, null, null, null);
weatherCursor.getCount(); //finally returns 0
当我尝试挖掘ContentResolver.notifyChange 的源代码时,我发现它尝试使用一个名为IContentObserver 的类,但无法解析导入。它在 try 块中执行此操作,而 catch 块是空的,所以它会默默地失败。
同样,AbstractCursor.setNotificationUri 调用ContentResolver.registerContentObserver,它尝试使用一个名为IContentService 的类,也在一个带有空catch 的try 块中。与上面不同,我什至没有看到IContentService 的导入语句。
评论者 (@Lawrence Choy) 解释说“IContentObserver 实际上是在IContentObserver.aidl 中定义的自动生成文件。”我在整个系统中搜索“IContentObserver.aidl”,唯一成功的是 Android Studio 用来存储最近搜索的文件,所以我的电脑上没有这个文件。我不确定该文件应该在哪里,但我尝试通过 SDK 管理器删除并重新安装构建工具和 API,但我仍然没有它。
[另一个编辑]
上面的 sn-p (在评估器中运行)旨在将以下所有代码归结为相关部分,但这里有一组更完整的代码。首先是deleteAllRecords() 的代码,这是我的一个测试单元中失败的方法。它成功删除了所有记录,但未能更新我认为理所当然的游标。
public void deleteAllRecords() {
Cursor weatherCursor = queryWholeTable(mContext, WeatherEntry.CONTENT_URI);
Cursor locationCursor = queryWholeTable(mContext, LocationEntry.CONTENT_URI);
int initialWeatherCount = weatherCursor.getCount();
int initialLocationCount = locationCursor.getCount();
int deletedWeatherCount = mContext.getContentResolver().delete(
WeatherEntry.CONTENT_URI,
null,
null
);
int finalWeatherCount = weatherCursor.getCount();
int deletedLocationCount = mContext.getContentResolver().delete(
LocationEntry.CONTENT_URI,
null,
null
);
int finalLocationCount = locationCursor.getCount();
assertEquals(initialWeatherCount, deletedWeatherCount);
//ASSERT BELOW FAILS!
assertEquals(0, finalWeatherCount);
weatherCursor.close();
assertEquals(initialLocationCount, deletedLocationCount);
//ASSERT BELOW FAILS!
assertEquals(0, finalLocationCount);
locationCursor.close();
//Added when above tests failed to see if records were actually deleted.
weatherCursor = queryWholeTable(mContext, WeatherEntry.CONTENT_URI);
locationCursor = queryWholeTable(mContext, LocationEntry.CONTENT_URI);
int weatherVeryFinal = weatherCursor.getCount();
int locationVeryFinal = locationCursor.getCount();
assertEquals(0, weatherVeryFinal);
assertEquals(0, locationVeryFinal);
locationCursor.close();
weatherCursor.close();
}
这里是.queryWholeTable:
public static Cursor queryWholeTable(Context context, Uri uri){
return context.getContentResolver().query(uri, null, null, null, null);
}
这里是WeatherProvider的.delete和.query方法:
@Override
public int delete(Uri targetUri, String selection, String[] selectionArgs) {
final int match = sUriMatcher.match(targetUri);
final String table;
final Uri notificationUri;
switch (match) {
case WEATHER:
table = WeatherEntry.TABLE_NAME;
notificationUri = WeatherEntry.CONTENT_URI;
break;
case LOCATION:
table = LocationEntry.TABLE_NAME;
notificationUri = LocationEntry.CONTENT_URI;
break;
default:
throw new UnsupportedOperationException("Unknown delete uri: " + targetUri.toString());
}
int affected = mOpenHelper.getWritableDatabase().delete(table, selection, selectionArgs);
if (selection == null || affected > 0) {
getContext().getContentResolver().notifyChange(notificationUri, null);
}
return affected;
}
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Here's the switch statement that, given a URI, will determine what kind of request it is,
// and query the database accordingly.
Cursor retCursor;
switch (sUriMatcher.match(uri)) {
// "weather/*/*"
case WEATHER_WITH_LOCATION_AND_DATE: {
retCursor = getWeatherByLocationSetting(uri, projection, sortOrder, true);
break;
}
// "weather/*"
case WEATHER_WITH_LOCATION: {
retCursor = getWeatherByLocationSetting(uri, projection, sortOrder, false);
break;
}
// "weather"
case WEATHER: {
retCursor = mOpenHelper.getReadableDatabase().query(
WeatherEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;
}
// "location/*"
case LOCATION_ID: {
retCursor = mOpenHelper.getReadableDatabase().query(
LocationEntry.TABLE_NAME,
projection,
LocationEntry._ID + " = " + ContentUris.parseId(uri),
selectionArgs,
null,
null,
sortOrder
);
break;
}
// "location"
case LOCATION: {
retCursor = mOpenHelper.getReadableDatabase().query(
LocationEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
retCursor.setNotificationUri(getContext().getContentResolver(), uri);
return retCursor;
}
如您所见,.setNotificationUri 总是在创建游标时调用,.notifyChange 总是在删除任何内容时调用。
【问题讨论】:
-
您是要覆盖
ContentResolver还是根本无法正常使用构建项目? -
我并没有尝试覆盖 ContentResolver,实际上该项目确实构建并运行,但我的光标没有收到更改通知,当我尝试查看
ContentResolver.notifyChange()的源时,我发现它叫 IContentObserver.notifyChange` 并且被难住了。这让我不确定是我的代码导致了问题,还是文件丢失。 -
我怀疑这不是您的问题的原因。
IContentObserver实际上是在IContentObserver.aidl中定义的自动生成文件。此文件是在您从 Android 源文件构建时生成的,您在 SDK 管理器中下载的源文件中可能不存在该文件。当您运行应用程序时,设备最终将拥有此文件。 -
谢谢!我编辑了问题。
-
与 sdk 捆绑的源代码仅供参考,从未构建。调用是在 frameworks.jar 中完成的,因此框架出现错误的可能性很小。你能把你注册回调的代码贴出来吗?
标签: android android-sqlite android-contentresolver aidl