SQLiteContentProvider.java revision 8402962ef58546d3cfd48fbb211b5e36df0f118e
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17package com.android.browser.provider; 18 19import android.content.ContentProvider; 20import android.content.ContentProviderOperation; 21import android.content.ContentProviderResult; 22import android.content.ContentValues; 23import android.content.Context; 24import android.content.OperationApplicationException; 25import android.database.sqlite.SQLiteDatabase; 26import android.database.sqlite.SQLiteOpenHelper; 27import android.database.sqlite.SQLiteTransactionListener; 28import android.net.Uri; 29 30import java.util.ArrayList; 31 32/** 33 * General purpose {@link ContentProvider} base class that uses SQLiteDatabase for storage. 34 */ 35public abstract class SQLiteContentProvider extends ContentProvider 36 implements SQLiteTransactionListener { 37 38 private static final String TAG = "SQLiteContentProvider"; 39 40 private SQLiteOpenHelper mOpenHelper; 41 private volatile boolean mNotifyChange; 42 protected SQLiteDatabase mDb; 43 44 private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>(); 45 private static final int SLEEP_AFTER_YIELD_DELAY = 4000; 46 47 /** 48 * Maximum number of operations allowed in a batch between yield points. 49 */ 50 private static final int MAX_OPERATIONS_PER_YIELD_POINT = 500; 51 52 @Override 53 public boolean onCreate() { 54 Context context = getContext(); 55 mOpenHelper = getDatabaseHelper(context); 56 return true; 57 } 58 59 /** 60 * Returns a {@link SQLiteOpenHelper} that can open the database. 61 */ 62 public abstract SQLiteOpenHelper getDatabaseHelper(Context context); 63 64 /** 65 * The equivalent of the {@link #insert} method, but invoked within a transaction. 66 */ 67 public abstract Uri insertInTransaction(Uri uri, ContentValues values, 68 boolean callerIsSyncAdapter); 69 70 /** 71 * The equivalent of the {@link #update} method, but invoked within a transaction. 72 */ 73 public abstract int updateInTransaction(Uri uri, ContentValues values, String selection, 74 String[] selectionArgs, boolean callerIsSyncAdapter); 75 76 /** 77 * The equivalent of the {@link #delete} method, but invoked within a transaction. 78 */ 79 public abstract int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, 80 boolean callerIsSyncAdapter); 81 82 /** 83 * Called when the provider needs to notify the system of a change. 84 * @param callerIsSyncAdapter true if the caller that caused the change was a sync adapter. 85 */ 86 public abstract void notifyChange(boolean callerIsSyncAdapter); 87 88 public boolean isCallerSyncAdapter(Uri uri) { 89 return false; 90 } 91 92 public SQLiteOpenHelper getDatabaseHelper() { 93 return mOpenHelper; 94 } 95 96 private boolean applyingBatch() { 97 return mApplyingBatch.get() != null && mApplyingBatch.get(); 98 } 99 100 @Override 101 public Uri insert(Uri uri, ContentValues values) { 102 Uri result = null; 103 boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); 104 boolean applyingBatch = applyingBatch(); 105 if (!applyingBatch) { 106 mDb = mOpenHelper.getWritableDatabase(); 107 mDb.beginTransactionWithListener(this); 108 try { 109 result = insertInTransaction(uri, values, callerIsSyncAdapter); 110 if (result != null) { 111 mNotifyChange = true; 112 } 113 mDb.setTransactionSuccessful(); 114 } finally { 115 mDb.endTransaction(); 116 } 117 118 onEndTransaction(callerIsSyncAdapter); 119 } else { 120 result = insertInTransaction(uri, values, callerIsSyncAdapter); 121 if (result != null) { 122 mNotifyChange = true; 123 } 124 } 125 return result; 126 } 127 128 @Override 129 public int bulkInsert(Uri uri, ContentValues[] values) { 130 int numValues = values.length; 131 boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); 132 mDb = mOpenHelper.getWritableDatabase(); 133 mDb.beginTransactionWithListener(this); 134 try { 135 for (int i = 0; i < numValues; i++) { 136 Uri result = insertInTransaction(uri, values[i], callerIsSyncAdapter); 137 if (result != null) { 138 mNotifyChange = true; 139 } 140 mDb.yieldIfContendedSafely(); 141 } 142 mDb.setTransactionSuccessful(); 143 } finally { 144 mDb.endTransaction(); 145 } 146 147 onEndTransaction(callerIsSyncAdapter); 148 return numValues; 149 } 150 151 @Override 152 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 153 int count = 0; 154 boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); 155 boolean applyingBatch = applyingBatch(); 156 if (!applyingBatch) { 157 mDb = mOpenHelper.getWritableDatabase(); 158 mDb.beginTransactionWithListener(this); 159 try { 160 count = updateInTransaction(uri, values, selection, selectionArgs, 161 callerIsSyncAdapter); 162 if (count > 0) { 163 mNotifyChange = true; 164 } 165 mDb.setTransactionSuccessful(); 166 } finally { 167 mDb.endTransaction(); 168 } 169 170 onEndTransaction(callerIsSyncAdapter); 171 } else { 172 count = updateInTransaction(uri, values, selection, selectionArgs, callerIsSyncAdapter); 173 if (count > 0) { 174 mNotifyChange = true; 175 } 176 } 177 178 return count; 179 } 180 181 @Override 182 public int delete(Uri uri, String selection, String[] selectionArgs) { 183 int count = 0; 184 boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); 185 boolean applyingBatch = applyingBatch(); 186 if (!applyingBatch) { 187 mDb = mOpenHelper.getWritableDatabase(); 188 mDb.beginTransactionWithListener(this); 189 try { 190 count = deleteInTransaction(uri, selection, selectionArgs, callerIsSyncAdapter); 191 if (count > 0) { 192 mNotifyChange = true; 193 } 194 mDb.setTransactionSuccessful(); 195 } finally { 196 mDb.endTransaction(); 197 } 198 199 onEndTransaction(callerIsSyncAdapter); 200 } else { 201 count = deleteInTransaction(uri, selection, selectionArgs, callerIsSyncAdapter); 202 if (count > 0) { 203 mNotifyChange = true; 204 } 205 } 206 return count; 207 } 208 209 @Override 210 public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) 211 throws OperationApplicationException { 212 int ypCount = 0; 213 int opCount = 0; 214 boolean callerIsSyncAdapter = false; 215 mDb = mOpenHelper.getWritableDatabase(); 216 mDb.beginTransactionWithListener(this); 217 try { 218 mApplyingBatch.set(true); 219 final int numOperations = operations.size(); 220 final ContentProviderResult[] results = new ContentProviderResult[numOperations]; 221 for (int i = 0; i < numOperations; i++) { 222 if (++opCount >= MAX_OPERATIONS_PER_YIELD_POINT) { 223 throw new OperationApplicationException( 224 "Too many content provider operations between yield points. " 225 + "The maximum number of operations per yield point is " 226 + MAX_OPERATIONS_PER_YIELD_POINT, ypCount); 227 } 228 final ContentProviderOperation operation = operations.get(i); 229 if (!callerIsSyncAdapter && isCallerSyncAdapter(operation.getUri())) { 230 callerIsSyncAdapter = true; 231 } 232 if (i > 0 && operation.isYieldAllowed()) { 233 opCount = 0; 234 if (mDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY)) { 235 ypCount++; 236 } 237 } 238 results[i] = operation.apply(this, results, i); 239 } 240 mDb.setTransactionSuccessful(); 241 return results; 242 } finally { 243 mApplyingBatch.set(false); 244 mDb.endTransaction(); 245 onEndTransaction(callerIsSyncAdapter); 246 } 247 } 248 249 @Override 250 public void onBegin() { 251 onBeginTransaction(); 252 } 253 254 @Override 255 public void onCommit() { 256 beforeTransactionCommit(); 257 } 258 259 @Override 260 public void onRollback() { 261 // not used 262 } 263 264 protected void onBeginTransaction() { 265 } 266 267 protected void beforeTransactionCommit() { 268 } 269 270 protected void onEndTransaction(boolean callerIsSyncAdapter) { 271 if (mNotifyChange) { 272 mNotifyChange = false; 273 notifyChange(callerIsSyncAdapter); 274 } 275 } 276} 277