Android数据库ContentProvider封装原理

大家都知道Android数据库使用的是SQLite,但是Google为了给我们简化操作,可以不用经常编写容易出错的SQL语句,直接通过 ContentProvider来封装数据的query查询、添加insert、删除delete和更新update,我们就以Android系统的 SDK中的例子来给大家简单的说明下吧。 public class NotePadProvider extends ContentProvider { private static final String TAG = “NotePadProvider”; private static final String DATABASE_NAME = “note_pad.db”; //数据库存储文件名,包含了.db后缀 private static final int DATABASE_VERSION = 2; //数据库版本号,这个是自己定义的,未来扩展数据库时自己可以方便的定义升级规则 private static final String NOTES_TABLE_NAME = “notes”; //表名 private static HashMap sNotesProjectionMap; //常规的Notes private static HashMap sLiveFolderProjectionMap; //LiveFoder内容 private static final int NOTES = 1; private static final int NOTE_ID = 2; private static final int LIVE_FOLDER_NOTES = 3; private static final UriMatcher sUriMatcher; //通常我们操作数据库的Uri比如content://android/cwj/1103这样的Uri均通过 UriMatcher注册并识别的。 private static class DatabaseHelper extends SQLiteOpenHelper { //数据库辅助子类 DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { //首次生成数据库,执行sql命令创建一个表 db.execSQL(“CREATE TABLE “ + NOTES_TABLE_NAME + “ (“ + Notes._ID + “ INTEGER PRIMARY KEY,” + Notes.TITLE + “ TEXT,” + Notes.NOTE + “ TEXT,” + Notes.CREATED_DATE + “ INTEGER,” + Notes.MODIFIED_DATE + “ INTEGER” + “);”); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //刚来数据的版本,就是为了定义我们如果未来数据库需要扩展,帮助用户识别并根据规则自动升级数据库文件 Log.w(TAG, “Upgrading database from version “ + oldVersion + “ to “ + newVersion + “, which will destroy all old data”); db.execSQL(“DROP TABLE IF EXISTS notes”); //由于这里没有做细节处理,如果有新版本,删除老的表,我们未来不能这样处理,这仅仅是Google的例子而已所以删除老版本数据 onCreate(db); } } private DatabaseHelper mOpenHelper; @Override public boolean onCreate() { //这里重写ContentProvider的onCreate方法做一些初始化操作 mOpenHelper = new DatabaseHelper(getContext()); return true; } //有关数据库的查询操作,Android的SQLite提供了一个SQLiteQueryBuilder方法再次将SQL命令封装了下,单独分离出表名,排序方法等 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(NOTES_TABLE_NAME); switch (sUriMatcher.match(uri)) { case NOTES: qb.setProjectionMap(sNotesProjectionMap); break; case NOTE_ID: qb.setProjectionMap(sNotesProjectionMap); qb.appendWhere(Notes._ID + “=” + uri.getPathSegments().get(1)); break; case LIVE_FOLDER_NOTES: qb.setProjectionMap(sLiveFolderProjectionMap); break; default: throw new IllegalArgumentException(“Unknown URI “ + uri); } String orderBy; if (TextUtils.isEmpty(sortOrder)) { orderBy = NotePad.Notes.DEFAULT_SORT_ORDER; } else { orderBy = sortOrder; } SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Override public String getType(Uri uri) { switch (sUriMatcher.match(uri)) { case NOTES: case LIVE_FOLDER_NOTES: return Notes.CONTENT_TYPE; case NOTE_ID: return Notes.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException(“Unknown URI “ + uri); } } 有关数据的插入操作,重写ContentProvider的insert方法即可 @Override public Uri insert(Uri uri, ContentValues initialValues) { if (sUriMatcher.match(uri) != NOTES) { throw new IllegalArgumentException(“Unknown URI “ + uri); } ContentValues values; if (initialValues != null) { values = new ContentValues(initialValues); } else { values = new ContentValues(); } Long now = Long.valueOf(System.currentTimeMillis()); if (values.containsKey(NotePad.Notes.CREATED_DATE) == false) { values.put(NotePad.Notes.CREATED_DATE, now); } if (values.containsKey(NotePad.Notes.MODIFIED_DATE) == false) { values.put(NotePad.Notes.MODIFIED_DATE, now); } if (values.containsKey(NotePad.Notes.TITLE) == false) { Resources r = Resources.getSystem(); values.put(NotePad.Notes.TITLE, r.getString(android.R.string.untitled)); } if (values.containsKey(NotePad.Notes.NOTE) == false) { values.put(NotePad.Notes.NOTE, “”); } SQLiteDatabase db = mOpenHelper.getWritableDatabase(); long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values); if (rowId > 0) { Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(noteUri, null); //通知数据库内容有改变 return noteUri; } throw new SQLException(“Failed to insert row into “ + uri); } @Override public int delete(Uri uri, String where, String[] whereArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case NOTES: count = db.delete(NOTES_TABLE_NAME, where, whereArgs); break; case NOTE_ID: String noteId = uri.getPathSegments().get(1); count = db.delete(NOTES_TABLE_NAME, Notes._ID + “=” + noteId + (!TextUtils.isEmpty(where) ? “ AND (“ + where + ‘)’ : “”), whereArgs); break; default: throw new IllegalArgumentException(“Unknown URI “ + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case NOTES: count = db.update(NOTES_TABLE_NAME, values, where, whereArgs); break; case NOTE_ID: String noteId = uri.getPathSegments().get(1); count = db.update(NOTES_TABLE_NAME, values, Notes._ID + “=” + noteId + (!TextUtils.isEmpty(where) ? “ AND (“ + where + ‘)’ : “”), whereArgs); break; default: throw new IllegalArgumentException(“Unknown URI “ + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } 最后我们需要在构造奔雷时就监听Uri,如果处理的Uri需要其他程序获知,需要在Androidmanifest.xml文件中显式的导出provider的Uri定义 static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(NotePad.AUTHORITY, “notes”, NOTES); sUriMatcher.addURI(NotePad.AUTHORITY, “notes/#”, NOTE_ID); sUriMatcher.addURI(NotePad.AUTHORITY, “live_folders/notes”, LIVE_FOLDER_NOTES); sNotesProjectionMap = new HashMap(); sNotesProjectionMap.put(Notes._ID, Notes._ID); sNotesProjectionMap.put(Notes.TITLE, Notes.TITLE); sNotesProjectionMap.put(Notes.NOTE, Notes.NOTE); sNotesProjectionMap.put(Notes.CREATED_DATE, Notes.CREATED_DATE); sNotesProjectionMap.put(Notes.MODIFIED_DATE, Notes.MODIFIED_DATE); // Support for Live Folders. sLiveFolderProjectionMap = new HashMap(); sLiveFolderProjectionMap.put(LiveFolders._ID, Notes._ID + “ AS “ + LiveFolders._ID); sLiveFolderProjectionMap.put(LiveFolders.NAME, Notes.TITLE + “ AS “ + LiveFolders.NAME); // Add more columns here for more robust Live Folders. } } 最后帮助初学者简单的概括下Android的ContentProvider类的作用,就是尽可能的减少SQL语句的编写在外部操作,封装成方法,而有关SQL语言的执行在DatabaseHelper中也被简化和分离出了,而SQL语句主要是体现在选择表的字段,where这样的条件限定语句大大减少了我们日常的开发。