Android:使用ContentResolver时的SQLite事务

目标:从XML数据刷新数据库

过程:

  • 开始交易
  • 从表中删除所有现有的行
  • 每parsingXML的每个主要元素插入主表中的行并获得PK
  • 主要元素的每个孩子logging插入第二个表中,提供来自上一步的FK
  • 提交事务

数据库操作相当标准的东西。 问题在于CRUD操作不是在ContentProvider完成的,而是使用ContentResolver因此插入例如看起来像resolver.insert(CONTENT_URI, contentValues) 。 ContentResolver API似乎没有任何事务相关的事情,我不能使用bulkInsert因为我间断地插入2个表(加上我想在事务中也有delete )。

我正在考虑通过使用registerContentObserver注册我自定义的ContentProvider作为监听器,但由于ContentResolver#acquireProvider方法是隐藏的,我如何获得正确的参考?

我运气不好吗?

Solutions Collecting From Web of "Android:使用ContentResolver时的SQLite事务"

我已经看到,在Google I / O应用程序的源代码中,它们覆盖了ContentProviderapplyBatch()方法并在其中使用了事务。 所以,你创build了一批ContentProviderOperation ,然后调用getContentResolver().applyBatch(uri_authority, batch)

我打算使用这种方法来看看它是如何工作的。 我很好奇,如果其他人已经尝试过。

正如kaciula所提到的那样,使用ContentProviderOperation可以很方便地完成基于事务的多表插入。

在构buildContentProviderOperation对象时,可以调用.withValueBackReference(fieldName,refNr)。 当使用applyBatch应用操作时,结果是由insert()调用提供的ContentValues对象将具有一个注入的整数。 该整数将用fieldNamestring键入,并且从先前应用的ContentProviderOperation的ContentProviderResult中检索其值,由refNr索引。

请参阅下面的代码示例。 在示例中,将一行插入到table1中,然后在表2中插入行时,将生成的ID(本例中为“1”)用作值。为了简洁,ContentProvider未连接到数据库。 在ContentProvider中,有打印输出可以添加事务处理。

 public class BatchTestActivity extends Activity { /** Called when the activity is first created. */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ArrayList<ContentProviderOperation> list = new ArrayList<ContentProviderOperation>(); list.add(ContentProviderOperation. newInsert(BatchContentProvider.FIRST_URI).build()); ContentValues cv = new ContentValues(); cv.put("name", "second_name"); cv.put("refId", 23); // In this example, "refId" in the contentValues will be overwritten by // the result from the first insert operation, indexed by 0 list.add(ContentProviderOperation. newInsert(BatchContentProvider.SECOND_URI). withValues(cv).withValueBackReference("refId", 0).build()); try { getContentResolver().applyBatch( BatchContentProvider.AUTHORITY, list); } catch (RemoteException e) { e.printStackTrace(); } catch (OperationApplicationException e) { e.printStackTrace(); } } } public class BatchContentProvider extends ContentProvider { private static final String SCHEME = "content://"; public static final String AUTHORITY = "com.test.batch"; public static final Uri FIRST_URI = Uri.parse(SCHEME + AUTHORITY + "/" + "table1"); public static final Uri SECOND_URI = Uri.parse(SCHEME + AUTHORITY + "/" + "table2"); public ContentProviderResult[] applyBatch( ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { System.out.println("starting transaction"); ContentProviderResult[] result; try { result = super.applyBatch(operations); } catch (OperationApplicationException e) { System.out.println("aborting transaction"); throw e; } System.out.println("ending transaction"); return result; } public Uri insert(Uri uri, ContentValues values) { // this printout will have a proper value when // the second operation is applied System.out.println("" + values); return ContentUris.withAppendedId(uri, 1); } // other overrides omitted for brevity } 

好吧 – 所以这不会漫不经心:我能想到的唯一方法是将startTransaction和endTransaction编码为基于URL的查询请求。 就像ContentResolver.query(START_TRANSACTION, null, null, null, null) 。 然后在ContentProvider#query根据注册的URL调用start或者end事务

你可以获得内容提供者对象本身的实现(如果在同一个进程中,提示:你可以通过multiprocess =“true”或者process =“”来控制提供者的进程) http://developer.android.com/guide/topics /manifest/provider-element.html )使用ContentProviderClient.getLocalContentProvider(),它可以被转换到您的提供程序实现,它可以提供额外的function,如closures和删除数据库的reset(),您也可以返回一个自定义的Transaction类实例save()和close()方法。

 public class Transaction { protected Transaction (SQLiteDatabase database) { this.database = database; database.beginTransaction (); } public void save () { this.database.setTransactionSuccessful (); } public void close () { this.database.endTransaction (); } private SQLiteDatabase database; } public Transaction createTransaction () { return new Transaction (this.dbHelper.getWritableDatabase ()); } 

然后:

 ContentProviderClient client = getContentResolver ().acquireContentProviderClient (Contract.authorityLocal); Transaction tx = ((LocalContentProvider) client.getLocalContentProvider ()).createTransaction ();