studio - update content provider android
Android: transacciones SQLite al usar ContentResolver (4)
El objetivo: actualizar la base de datos a partir de datos XML
El proceso:
- Comience la transacción
- Eliminar todas las filas existentes de las tablas
- Por cada elemento principal de la fila de inserción XML analizada en la tabla principal y obtener PK
- Por cada elemento secundario del elemento principal, inserte el registro en la segunda tabla que proporciona FK del paso anterior
- Transacción de compromiso
Bastante estándar en cuanto a operaciones de DB. El problema es que las operaciones CRUD no se realizan dentro de ContentProvider
sino que usan ContentResolver
por lo que la inserción, por ejemplo, se ve como resolver.insert(CONTENT_URI, contentValues)
. La API de ContentResolver no parece tener nada relacionado con la transacción y no puedo usar bulkInsert
ya que estoy insertando en 2 tablas de forma intermitente (además de que también quiero delete
dentro de la transacción).
Estaba pensando en registrar mi ContentProvider
personalizado como oyente utilizando registerContentObserver
pero como los métodos de ContentResolver#acquireProvider
están ocultos, ¿cómo ContentResolver#acquireProvider
la referencia correcta?
¿No tengo suerte?
Es posible realizar inserciones de tabla múltiple basadas en transacciones de forma bastante limpia desde Android 2.1 utilizando ContentProviderOperation, como lo menciona kaciula.
Cuando crea el objeto ContentProviderOperation, puede llamar a .withValueBackReference (fieldName, refNr). Cuando la operación se aplica con applyBatch, el resultado es que el objeto ContentValues que se suministra con la llamada insert () tendrá un entero insertado. El entero se tecleará con fieldName String, y su valor se recuperará del ContentProviderResult de un ContentProviderOperation aplicado previamente, indexado por refNr.
Por favor refiérase al ejemplo de código a continuación. En el ejemplo, se inserta una fila en la tabla 1, y la ID resultante (en este caso, "1") se usa como un valor al insertar la fila en la tabla 2. Por brevedad, ContentProvider no está conectado a una base de datos. En ContentProvider, hay impresiones donde sería adecuado agregar el manejo de transacciones.
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
}
Está bien, así que esto no se desvanece sin rumbo: la única forma en que puedo pensar es codificar startTransaction y endTransaction como solicitudes de consulta basadas en URL. Algo así como ContentResolver.query(START_TRANSACTION, null, null, null, null)
. Luego, en la ContentProvider#query
basada en la transacción de inicio o finalización de la llamada a la URL registrada
He visto que en el código fuente de la aplicación Google I / O, anulan el método applyBatch()
ContentProvider
y usan transacciones dentro de él. Entonces, crea un lote de ContentProviderOperation
y luego llama a getContentResolver().applyBatch(uri_authority, batch)
.
Estoy planeando usar este enfoque para ver cómo funciona. Tengo curiosidad si alguien más lo ha intentado.
Puede obtener la implementación del objeto del proveedor de contenido en sí (si está en el mismo proceso, sugerencia: puede controlar el proceso del proveedor con multiprocess = "true" o process = "" http://developer.android.com/guide/topics/manifest/provider-element.html ) utilizando ContentProviderClient.getLocalContentProvider () que se puede convertir en su implementación de proveedor que puede proporcionar funcionalidad adicional como un restablecimiento () que cierra y elimina la base de datos y también puede devolver una instancia de clase de transacción personalizada métodos save () y 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 ());
}
Entonces:
ContentProviderClient client = getContentResolver ().acquireContentProviderClient (Contract.authorityLocal);
Transaction tx = ((LocalContentProvider) client.getLocalContentProvider ()).createTransaction ();