2014-04-30 12 views
0

我有一個內容提供商支持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執行之前完成的?

背景: 有從表詳細信息掌握一門重要的參考,我得到了一個bug報告

SQLiteConstraintException: foreign key constraint failed (code 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) 
+0

您確定沒有其他應用程序或線程同時訪問提供程序嗎?另一個線程可能會在上述兩個調用之間進行調用,修改MASTER表的方式會導致第2行調用失敗。 –

+0

只有一個應用程序正在訪問此數據庫。但我不能排除,因爲主要活動有[launchMode singleTop而不是singleTask](http://stackoverflow.com/q/16693191/2306907),所以不存在應用程序的另一個實例。但我無法想象兩個實例修改此表的實際情況,因爲這都是用戶驅動的。因此,您將不得不以光速切換屏幕,因爲不存在添加數據的後臺進程。 –

+0

要回答你的原始問題:是的,你的兩條線將在你的線程中順序運行。與「ContentProvider」的通信發生在屏幕後面的「Binder」調用。在ContentProvider方面,它應該也是順序執行的。但是,這取決於實施。如果它正在做一些事情,比如產生一個線程來處理刪除操作,那麼它可能是爭用的原因。雖然這可能不太可能。有一件事我不明白:你的MASTER表有什麼外鍵? –

回答

4

ContentResolver對象使用BinderIBinder)接口幕後與溝通個實例。 Binder調用被阻止,所以它們將在您的應用程序中順序執行。檢查AOSP來源:ContentResolverIContentProviderIContentProvider是一個同步Binder接口,沒有async關鍵字應用於AIDL定義的接口。

此外,請檢查外鍵的SQLite文檔。如果您嘗試刪除MASTER表中的所有行,但在DETAIL表中仍存在一行引用MASTER表ID的行,則它將失敗:sqlite.org/foreignkeys.html

+0

關於第二段:這是錯誤的唯一解釋:DETAIL表中仍存在一行。如果這兩個調用按順序執行,則必須有來自另一個進程添加此行的進程/線程的調用。 –

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 

更多info:http://www.sqlite.org/foreignkeys.html#fk_actions

+0

我知道這種可能性。但最初的問題依然存在。這種呼叫可能無序執行將對我的整個項目產生巨大影響。 –

相關問題