是否保证(在一个线程内)对内容提供者的两次连续调用的执行顺序?

时间:2014-04-30 12:26:21

标签: android sqlite android-contentprovider

我有一个由SQLiteDatabase支持的内容提供商。请考虑以下两个调用:

//the following call results in "DELETE FROM DETAILS":
context.getContentResolver().delete(getContentUriOfDetailTable(), null, null);   //line 1
//the following call results in "DELETE FROM MASTER": 
context.getContentResolver().delete(getContentUriOfMasterTable(), null, null);   //line 2

据我所知,内容提供商are thread safe, when backed by an SQLiteDatabase。但有没有保证,第1行触发的SQL的执行在第2行触发的SQL执行之前就完成了?

背景 从表DETAIL到MASTER有一个外键引用,我得到了一个bug报告

  

SQLiteConstraintException:外键约束失败(代码19)

来自第2行,但我无法重现它。

表:

CREATE TABLE MASTER 
(_id PRIMARY KEY autoincrement,
VALUE text
);

CREATE TABLE DETAILS (
_id PRIMARY KEY autoincrement,
MASTERIDX integer not null,
VALUE text,
FOREIGN KEY(MASTERIDX) REFERENCES MASTER(_id)
);

我的ContentProvider没什么特别的:

public class MyDbContentProvider extends ContentProvider {

    private MyDbOpenHelper mDbHelper;

    //...
    //...

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = mDbHelper.getWritableDatabase();
        int rowsDeleted = 0;
        String where;

        MyUriMatcher.MatchResult matchRes = mURIMatcher.matchTable(uri);
        TableDef tableDef = matchRes.tableDef;

        switch (matchRes.uriType) {
        case ALL_ITEMS:
            where = selection;
            break;
        case SINGLE_ITEM:
            String idstr = uri.getLastPathSegment();
            where = TableDef._ID + " = " + idstr;
            if (!TextUtils.isEmpty(selection)) {
                where += " AND (" + selection + ")";
            }
            break;
        default:
            throw new IllegalArgumentException("Unsupported URI for delete: " + uri);
        }

        try {
            rowsDeleted = db.delete(tableDef.getTableName(), where, selectionArgs);
        } catch (SQLException e) {
            throw createCustomSqlException(e, "DELETE", uri, null, where, selectionArgs);
        }       
        // notify all listeners of changes:
        if (rowsDeleted > 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return rowsDeleted;
    }
}

这是堆栈跟踪:

android.database.SQLException: SQLiteConstraintException: foreign key constraint failed (code 19) for DELETE for following URI: content://de.myapp.mobil.myappDbProvider/master and this selection: null
at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)
at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:858)
at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:754)
at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:64)
at android.database.sqlite.SQLiteDatabase.delete(SQLiteDatabase.java:1494)
at de.myapp.mobil.MyDbContentProvider.delete(MyDbContentProvider.java:267)
at android.content.ContentProvider$Transport.delete(ContentProvider.java:228)
at android.content.ContentResolver.delete(ContentResolver.java:958)
at de.myapp.CommonDbContract$TableDef.deleteAll(CommonDbContract.java:119)
at de.myapp.mobil.MyDbContract.deleteAll(MyDbContract.java:1730)
at de.myapp.mobil.MyDbContract.recreateDbOrDeleteAll(MyDbContract.java:1761)
at de.myapp.mobil.SettingsActivity$ResetAllCommand.execute(SettingsActivity.java:77)
at de.myapp.mobil.SettingsActivity$ResetAllCommand.execute(SettingsActivity.java:1)
at de.myapp.DlgUtils$DataCommand.execute(DlgUtils.java:54)
at de.myapp.DlgUtils$CombinedCommand.execute(DlgUtils.java:116)
at de.myapp.DlgUtils$CommandWrapper.onClick(DlgUtils.java:157)

2 个答案:

答案 0 :(得分:4)

ContentResolver个对象在幕后使用BinderIBinder)接口与ContentProvider个实例进行通信。 Binder来电被阻止,因此它们会在您的应用中按顺序执行。检查AOSP来源:ContentResolverIContentProviderIContentProvider是一个同步Binder接口,没有async关键字应用于AIDL定义的接口。

另外,检查外键上的SQLite文档。如果您尝试从MASTER表中删除所有行,但在DETAIL表中仍然存在引用MASTER表ID的行,那么它将失败:sqlite.org/foreignkeys.html

答案 1 :(得分:0)

而不是这个

CREATE TABLE MASTER 
(_id PRIMARY KEY autoincrement,
VALUE text
);

CREATE TABLE DETAILS (
_id PRIMARY KEY autoincrement,
MASTERIDX integer not null,
VALUE text,
FOREIGN KEY(MASTERIDX) REFERENCES MASTER(_id)
);

只需更新此

CREATE TABLE MASTER 
(_id PRIMARY KEY autoincrement,
VALUE text
);

CREATE TABLE DETAILS (
_id PRIMARY KEY autoincrement,
MASTERIDX integer not null,
VALUE text,
FOREIGN KEY(MASTERIDX) REFERENCES MASTER(_id) ON DELETE CASCADE
);

现在,这一行足以删除主表和详细信息表中的数据:

//the following call results in "DELETE FROM MASTER": 
context.getContentResolver().delete(getContentUriOfMasterTable(), null, null);   //line 2

了解更多信息:http://www.sqlite.org/foreignkeys.html#fk_actions