الروبوت:سكليتي المعاملات عند استخدام ContentResolver
سؤال
الهدف:تحديث قاعدة البيانات من بيانات XML
العملية:
- تبدأ الصفقة
- حذف كافة الصفوف الموجودة من الجداول
- لكل العنصر الرئيسي من تحليل XML إدراج صف في الجدول الرئيسي والحصول على PK
- في كل طفل من العنصر الرئيسي إدراج سجل في الجدول 2 توفير FK من الخطوة السابقة
- ارتكاب الصفقة
جميلة القياسية الاشياء بقدر db العمليات.المشكلة هي أن عمليات الخام لا تتم داخل ContentProvider
ولكن بدلا من ذلك باستخدام ContentResolver
حتى أدخل على سبيل المثال يبدو resolver.insert(CONTENT_URI, contentValues)
.على ContentResolver API لا يبدو أن يكون أي شيء يخص المعاملات وأنا لا يمكن استخدام bulkInsert
أنا منذ أن تدرج في الجداول 2 بشكل متقطع (زائد أريد أن يكون delete
داخل الصفقة كذلك).
كنت أفكر في تسجيل بلدي حسب الطلب ContentProvider
كما المستمع باستخدام registerContentObserver
ولكن منذ ContentResolver#acquireProvider
طرق مخفية كيف يمكنني الحصول على حق مرجع ؟
أنا من الحظ ؟
المحلول
لقد رأيت ذلك في التعليمات البرمجية المصدر لتطبيق Google I / O، تجاوزها ContentProvider
'س applyBatch()
طريقة واستخدام المعاملات داخلها. لذلك، يمكنك إنشاء دفعة من ContentProviderOperation
S ثم اتصل getContentResolver().applyBatch(uri_authority, batch)
.
أخطط لاستخدام هذا النهج لمعرفة كيف يعمل. أنا فضولي إذا حاول أي شخص آخر ذلك.
نصائح أخرى
فمن الممكن أن تفعل الصفقة على أساس متعدد الجدول إدراج بدلا نظيفة منذ الروبوت 2.1 باستخدام ContentProviderOperation ، كما ذكر kaciula.
عند بناء ContentProviderOperation كائن يمكنك الاتصال .withValueBackReference(fieldName, refNr).عند تشغيل تطبيق باستخدام applyBatch ، والنتيجة هي أن ContentValues الكائن الذي تم توفيره مع إدراج() الدعوة سوف يكون عدد صحيح حقن.صحيح سوف يكون مرتبطا مع fieldName سلسلة وقيمته يتم استردادها من ContentProviderResult سابقا تطبيق ContentProviderOperation ، فهرستها من قبل refNr.
يرجى الرجوع إلى نموذج التعليمات البرمجية أدناه.في العينة ، على التوالي يتم إدراجها في جدول1 ، مما أدى معرف (في هذه الحالة "1") هو قيمة عند إدراج صف في الجدول 2.الإيجاز ، 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
}
حسنا - لذلك هذا لا dengle بلا هدف: الطريقة الوحيدة التي يمكنني التفكير فيها هي إبرام إيقاف التشغيل وإنشاءات الإنتاجية كطلبات استعلام قائمة على المواقع URL. شيء مثل ContentResolver.query(START_TRANSACTION, null, null, null, null)
. وبعد ثم في ContentProvider#query
استنادا إلى بدء أو نهاية عنوان URL المسجل
يمكنك الحصول على تطبيق كائن موفر المحتوى نفسه (إذا كان في نفس العملية، تلميح: يمكنك التحكم في عملية المزود باستخدام MultipRocess = "True" أو العملية = "" http://developer.android.com/guide/topics/manifest/provider-element.html.) استخدام contentproviderclient.getlocalcontentprovider () والتي يمكن صيدها على تنفيذ المزود الخاص بك والتي يمكن أن توفر وظائف إضافية مثل إعادة تعيين () التي تغلق وحذف قاعدة البيانات ويمكنك أيضا إرجاع مثيل فئة المعاملات المخصصة مع طرق حفظ () وإغلاق () وبعد
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 ();