创建并使用一个地震Content Provider
已经创建了一个应用程序,它能够显示最近的地震列表。现在,你有一个极好的机会来和其它应用程序共享这些信息。
通过一个Content Provider来暴露这些数据,你或其他人都可以创建基于这些数据的应用程序,而不用加倍网络流量和相关的XML解析。
创建Content Provider
接下来的例子显示了如何创建一个地震Content Provider。每一个quake都将储存在一个SQLite数据库。
1. 打开Earthquake工程,创建一个新的EarthquakeProvider类,扩展Content Provider。重写onCreate、getType、query、insert、delete和update方法。
package com.paad.earthquake;
import android.content.*;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
public class EarthquakeProvider extends ContentProvider {
@Override
public boolean onCreate() {
}
@Override
public String getType(Uri url) {
}
@Override
public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort) {
}
@Override
public Uri insert(Uri _url, ContentValues _initialValues) {
}
@Override
public int delete(Uri url, String where, String[] whereArgs) {
}
@Override
public int update(Uri url, ContentValues values, String where, String[] wArgs) {
}
}
2. 为这个Provider定义一个Content URI。这个URI将用于在程序组件中,使用ContentResolver来访问这个Content Provider。
public static final Uri CONTENT_URI = Uri.parse(“content://com.paad.provider.earthquake/earthquakes”);
3. 创建数据库,用来存储地震数据。在EarthquakeProvider类中,创建一个新的SQLiteDatabase实例,并且暴露列名称和索引的公共变量。包含一个SQLiteOpenHelper的扩展类来管理数据库的创建和版本控制。
// 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);
}
}
4. 创建一个UriMatcher来处理不同不同的URI请求。包含对整个数据集(QUAKES)和单个记录的索引(QUAKE_ID)的查询和交互的支持。
// 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);
}
5. 重写getType方法来返回每个支持的URI结构的字符串。
@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);
}
}
6. 重写Provider的onCreate方法,来创建一个数据库辅助类的实例,并打开一个数据库的连接。
@Override
public boolean onCreate() {
Context context = getContext();
earthquakeDatabaseHelper dbHelper;
dbHelper = new earthquakeDatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
earthquakeDB = dbHelper.getWritableDatabase();
return (earthquakeDB == null) ? false : true;
}
7. 实现查询和交互的函数。首先是查询方法;它应该解析发送的请求(是所有的内容还是单行),并应用selection,projection和sortorder等参数来返回一个Cursor。
@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;
}
8. 现在实现insert、delete和update方法。在这种情况下,这个过程基本上是练习将Content Provider的交互请求映射到数据库上。
@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;
}
9. 当Content Provider完成时,在程序的manifest的application标签中添加一个节点来注册它。
<provider android:name=”.EarthquakeProvider” android:authorities=”com.paad.provider.earthquake” />
使用Provider
现在,你可以更新Earthquake Activity来使用EarthquakeProvider来储存quake,并使用它们来填入ListView。
1. 在Earthquake Activity中,更新addNewQuake方法。它应该使用程序的ContentResolver来插入每个地震信息。把现有的数组控制逻辑移动到新的addQuakeToArray方法中。
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.
Cursor c = cr.query(EarthquakeProvider.CONTENT_URI,null, w, null, null);
int dbCount = c.getCount();
c.close();
if (dbCount > 0) (应该是 == 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);
}
}
private void addQuakeToArray(Quake _quake) {
if (_quake.getMagnitude() > minimumMagnitude)
{
// Add the new quake to our list of earthquakes.
earthquakes.add(_quake);
// Notify the array adapter of a change.
aa.notifyDataSetChanged();
}
}
2. 创建一个loadQuakesFromProvider方法来从EarthquakeProvider加载所有的地震数据,并且使用addQuakeToArray方法来将它们插入到数组列表中。
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;
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());
}
c.close();
}
3. 在onCreate方法中调用loadQuakeFromProvider方法来初始化Earthquake的ListView。
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
earthquakeListView =
(ListView)this.findViewById(R.id.earthquakeListView);
earthquakeListView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView _av, View _v, int _index, long arg3) {
selectedQuake = earthquakes.get(_index);
showDialog(QUAKE_DIALOG);
}
});
int layoutID = android.R.layout.simple_list_item_1;
aa = new ArrayAdapter<Quake>(this, layoutID , earthquakes);
earthquakeListView.setAdapter(aa);
loadQuakesFromProvider();
updateFromPreferences();
refreshEarthquakes();
}
4. 最后,对refreshEarthquake方法做一项变更,在清除数组之后,添加新的quake之前,加载保存的地震数据。
private void refreshEarthquakes() {
[ ... exiting refreshEarthquakes method ... ]
// Clear the old earthquakes
earthquakes.clear();
loadQuakesFromProvider();
[ ... exiting refreshEarthquakes method ... ]
}