32

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

过程:

  • 开始交易
  • 从表中删除所有现有行
  • 每个已解析 XML 的每个主要元素都行插入主表并获取 PK
  • 每个主元素的每个子元素都将记录插入到第二个表中,从上一步提供 FK
  • 提交事务

就数据库操作而言,相当标准的东西。问题是 CRUD 操作不是在内部完成,ContentProvider而是在使用ContentResolver,因此例如插入看起来像resolver.insert(CONTENT_URI, contentValues). ContentResolver API 似乎与事务无关,我无法使用bulkInsert,因为我间歇性地插入 2 个表(另外我也想delete在事务中插入)。

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

我运气不好?

4

4 回答 4

41

我已经看到在 Google I/O 应用程序的源代码中,它们覆盖了ContentProvider'sapplyBatch()方法并在其中使用事务。因此,您创建了一批ContentProviderOperations 然后调用getContentResolver().applyBatch(uri_authority, batch).

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

于 2010-10-15T15:56:13.077 回答
15

正如 kaciula 所提到的,从 Android 2.1 开始,通过使用 ContentProviderOperation 可以相当干净地进行基于事务的多表插入。

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

请参考下面的代码示例。在示例中,在表 1 中插入了一行,然后在表 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
}
于 2011-11-17T13:45:11.590 回答
4

好吧 - 所以这不会漫无目的:我能想到的唯一方法是将 startTransaction 和 endTransaction 编码为基于 URL 的查询请求。类似的东西ContentResolver.query(START_TRANSACTION, null, null, null, null)。然后在ContentProvider#query基于注册的 URL 调用开始或结束事务

于 2010-03-10T18:16:30.557 回答
0

可以获取content provider对象本身的实现(如果在同一个进程中,提示:可以用multiprocess="true"或者process=""来控制provider的进程http://developer.android.com/guide/topics /manifest/provider-element.html ) 使用 ContentProviderClient.getLocalContentProvider () 可以转换为您的提供程序实现,它可以提供额外的功能,如关闭和删除数据库的 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 ();
于 2013-12-30T07:05:48.443 回答