SQLite
游标(Cursor)相当于指向底层数据中结果集的指针,而不是提取和返回结果值的副本,是在结果集中对位置(行)进行控制的管理方式。
moveToFirst:把游标移动到查询结果的第一行
moveToNext:把游标移动到下一行
moveToPrevious:把游标移动到前一行
getCount:返回结果集中的行数
getColumnIndexOrThrow:返回具有指定名称的列的索引
getColumnName:返回指定索引的列的名称
getColumnNames:返回当前游标中的所有列名的字符串数组
moveToPosition:移动到特定的行的游标
getPosition:返回当前的游标位置
Adapter类负责管理数据库交互(数据模型,更具对象属性设置增删改查的逻辑),使用SQLiteOpenHelper的扩展类协助创建数据表,获取SQLiteDatabase对象进行增删改查。
扩展SQLiteOpenHelper,由扩展类完成数据表的创建,数据库的更新。
package com.paad.todolist; import java.util.Date; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.util.Log; public class ToDoDBAdapter { private static final String DATABASE_NAME = "todoList.db"; private static final String DATABASE_TABLE = "todoItems"; private static final int DATABASE_VERSION = 1; public static final String KEY_ID = "_id"; public static final String KEY_TASK = "task"; public static final String KEY_CREATION_DATE = "creation_date"; private SQLiteDatabase db; private final Context context; private toDoDBOpenHelper dbHelper; public ToDoDBAdapter(Context _context) { this.context = _context; dbHelper = new toDoDBOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION); } public void close() { db.close(); } public void open() throws SQLiteException { try { db = dbHelper.getWritableDatabase(); } catch (SQLiteException ex) { db = dbHelper.getReadableDatabase(); } } //Insert a new task public long insertTask(ToDoItem _task) { // Create a new row of values to insert. ContentValues newTaskValues = new ContentValues(); // Assign values for each row. newTaskValues.put(KEY_TASK, _task.getTask()); newTaskValues.put(KEY_CREATION_DATE, _task.getCreated().getTime()); // Insert the row. return db.insert(DATABASE_TABLE, null, newTaskValues); } // Remove a task based on its index public boolean removeTask(long _rowIndex) { return db.delete(DATABASE_TABLE, KEY_ID + "=" + _rowIndex, null) > 0; } // Update a task public boolean updateTask(long _rowIndex, String _task) { ContentValues newValue = new ContentValues(); newValue.put(KEY_TASK, _task); return db.update(DATABASE_TABLE, newValue, KEY_ID + "=" + _rowIndex, null) > 0; } public Cursor getAllToDoItemsCursor() { return db.query(DATABASE_TABLE, new String[] { KEY_ID, KEY_TASK, KEY_CREATION_DATE}, null, null, null, null, null); } public Cursor setCursorToToDoItem(long _rowIndex) throws SQLException { Cursor result = db.query(true, DATABASE_TABLE, new String[] {KEY_ID, KEY_TASK}, KEY_ID + "=" + _rowIndex, null, null, null, null, null); if ((result.getCount() == 0) || !result.moveToFirst()) { throw new SQLException("No to do items found for row: " + _rowIndex); } return result; } public ToDoItem getToDoItem(long _rowIndex) throws SQLException { Cursor cursor = db.query(true, DATABASE_TABLE, new String[] {KEY_ID, KEY_TASK}, KEY_ID + "=" + _rowIndex, null, null, null, null, null); if ((cursor.getCount() == 0) || !cursor.moveToFirst()) { throw new SQLException("No to do item found for row: " + _rowIndex); } String task = cursor.getString(cursor.getColumnIndex(KEY_TASK)); long created = cursor.getLong(cursor.getColumnIndex(KEY_CREATION_DATE)); ToDoItem result = new ToDoItem(task, new Date(created)); return result; } private static class toDoDBOpenHelper extends SQLiteOpenHelper { public toDoDBOpenHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } // SQL Statement to create a new database. private static final String DATABASE_CREATE = "create table " + DATABASE_TABLE + " (" + KEY_ID + " integer primary key autoincrement, " + KEY_TASK + " text not null, " + KEY_CREATION_DATE + " long);"; @Override public void onCreate(SQLiteDatabase _db) { _db.execSQL(DATABASE_CREATE); } @Override public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion) { Log.w("TaskDBAdapter", "Upgrading from version " + _oldVersion + " to " + _newVersion + ", which will destroy all old data"); // Drop the old table. _db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE); // Create a new one. onCreate(_db); } } }
Content
ContentProvider可使用SQLiteOpenHelper辅助操作数据(用数据库持久化)。由ContentProvider的扩展类完成数据交互,ContentProvider扩展类的对象则是通过获取到应用上下文中的ContentResolver对象,根据其方法及URI,由系统内部执行ContentProvider扩展类对象中重写的方法。
ContentProvider扩展类的实现:
public class EarthquakeProvider extends ContentProvider { public static final Uri CONTENT_URI = Uri.parse("content://com.paad.provider.earthquake/earthquakes"); @Override public boolean onCreate() { Context context = getContext(); earthquakeDatabaseHelper dbHelper = new earthquakeDatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION); earthquakeDB = dbHelper.getWritableDatabase(); return (earthquakeDB == null) ? false : true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sort) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(EARTHQUAKE_TABLE); // If this is a row query, limit the result set to the passed in row. switch (uriMatcher.match(uri)) { case QUAKE_ID: qb.appendWhere(KEY_ID + "=" + uri.getPathSegments().get(1)); break; default : break; } // If no sort order is specified sort by date / time String orderBy; if (TextUtils.isEmpty(sort)) { orderBy = KEY_DATE; } else { orderBy = sort; } // Apply the query to the underlying database. Cursor c = qb.query(earthquakeDB, projection, selection, selectionArgs, null, null, orderBy); // Register the contexts ContentResolver to be notified if // the cursor result set changes. c.setNotificationUri(getContext().getContentResolver(), uri); // Return a cursor to the query result. return c; } @Override public Uri insert(Uri _uri, ContentValues _initialValues) { // Insert the new row, will return the row number if // successful. long rowID = earthquakeDB.insert(EARTHQUAKE_TABLE, "quake", _initialValues); // Return a URI to the newly inserted row on success. if (rowID > 0) { Uri uri = ContentUris.withAppendedId(CONTENT_URI, rowID); getContext().getContentResolver().notifyChange(uri, null); return uri; } throw new SQLException("Failed to insert row into " + _uri); } @Override public int delete(Uri uri, String where, String[] whereArgs) { int count; switch (uriMatcher.match(uri)) { case QUAKES: count = earthquakeDB.delete(EARTHQUAKE_TABLE, where, whereArgs); break; case QUAKE_ID: String segment = uri.getPathSegments().get(1); count = earthquakeDB.delete(EARTHQUAKE_TABLE, KEY_ID + "=" + segment + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { int count; switch (uriMatcher.match(uri)) { case QUAKES: count = earthquakeDB.update(EARTHQUAKE_TABLE, values, where, whereArgs); break; case QUAKE_ID: String segment = uri.getPathSegments().get(1); count = earthquakeDB.update(EARTHQUAKE_TABLE, values, KEY_ID + "=" + segment + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case QUAKES: return "vnd.android.cursor.dir/vnd.paad.earthquake"; case QUAKE_ID: return "vnd.android.cursor.item/vnd.paad.earthquake"; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } } // Create the constants used to differentiate between the different URI // requests. private static final int QUAKES = 1; private static final int QUAKE_ID = 2; private static final UriMatcher uriMatcher; // Allocate the UriMatcher object, where a URI ending in 'earthquakes' will // correspond to a request for all earthquakes, and 'earthquakes' with a // trailing '/[rowID]' will represent a single earthquake row. static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI("com.paad.provider.Earthquake", "earthquakes", QUAKES); uriMatcher.addURI("com.paad.provider.Earthquake", "earthquakes/#", QUAKE_ID); } //The underlying database private SQLiteDatabase earthquakeDB; private static final String TAG = "EarthquakeProvider"; private static final String DATABASE_NAME = "earthquakes.db"; private static final int DATABASE_VERSION = 1; private static final String EARTHQUAKE_TABLE = "earthquakes"; // Column Names public static final String KEY_ID = "_id"; public static final String KEY_DATE = "date"; public static final String KEY_DETAILS = "details"; public static final String KEY_LOCATION_LAT = "latitude"; public static final String KEY_LOCATION_LNG = "longitude"; public static final String KEY_MAGNITUDE = "magnitude"; public static final String KEY_LINK = "link"; // Column indexes public static final int DATE_COLUMN = 1; public static final int DETAILS_COLUMN = 2; public static final int LONGITUDE_COLUMN = 3; public static final int LATITUDE_COLUMN = 4; public static final int MAGNITUDE_COLUMN = 5; public static final int LINK_COLUMN = 6; // Helper class for opening, creating, and managing database version control private static class earthquakeDatabaseHelper extends SQLiteOpenHelper { private static final String DATABASE_CREATE = "create table " + EARTHQUAKE_TABLE + " (" + KEY_ID + " integer primary key autoincrement, " + KEY_DATE + " INTEGER, " + KEY_DETAILS + " TEXT, " + KEY_LOCATION_LAT + " FLOAT, " + KEY_LOCATION_LNG + " FLOAT, " + KEY_MAGNITUDE + " FLOAT), " + KEY_LINK + " TEXT);"; public earthquakeDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DATABASE_CREATE); } @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 " + EARTHQUAKE_TABLE); onCreate(db); } } }
ContentProvider扩展类的使用:
private void loadQuakesFromProvider() { // Clear the existing earthquake array earthquakes.clear(); ContentResolver cr = getContentResolver(); // Return all the saved earthquakes Cursor c = cr.query(EarthquakeProvider.CONTENT_URI, null, null, null, null); if (c.moveToFirst()) { do { // Extract the quake details. Long datems = c.getLong(EarthquakeProvider.DATE_COLUMN); String details = c.getString(EarthquakeProvider.DETAILS_COLUMN); Float lat = c.getFloat(EarthquakeProvider.LATITUDE_COLUMN); Float lng = c.getFloat(EarthquakeProvider.LONGITUDE_COLUMN); Double mag = c.getDouble(EarthquakeProvider.MAGNITUDE_COLUMN); String link = c.getString(EarthquakeProvider.LINK_COLUMN); Location location = new Location("dummy"); location.setLongitude(lng); location.setLatitude(lat); Date date = new Date(datems); Quake q = new Quake(date, details, location, mag, link); addQuakeToArray(q); } while(c.moveToNext()); } } private void addNewQuake(Quake _quake) { ContentResolver cr = getContentResolver(); // Construct a where clause to make sure we don�t already have this // earthquake in the provider. String w = EarthquakeProvider.KEY_DATE + " = " + _quake.getDate().getTime(); // If the earthquake is new, insert it into the provider. if (cr.query(EarthquakeProvider.CONTENT_URI, null, w, null, null).getCount()==0){ ContentValues values = new ContentValues(); values.put(EarthquakeProvider.KEY_DATE, _quake.getDate().getTime()); values.put(EarthquakeProvider.KEY_DETAILS, _quake.getDetails()); double lat = _quake.getLocation().getLatitude(); double lng = _quake.getLocation().getLongitude(); values.put(EarthquakeProvider.KEY_LOCATION_LAT, lat); values.put(EarthquakeProvider.KEY_LOCATION_LNG, lng); values.put(EarthquakeProvider.KEY_LINK, _quake.getLink()); values.put(EarthquakeProvider.KEY_MAGNITUDE, _quake.getMagnitude()); cr.insert(EarthquakeProvider.CONTENT_URI, values); earthquakes.add(_quake); addQuakeToArray(_quake); } }