content provider 和content resolver提供了用于各个application之间互相访问数据但又保持独立的机制。android系统已经提供的一些content provider有:Media store, Browser, contact, calendar和call log.
- content provider 创建步骤
Step #1: 创建SQLite数据库
如果手工创建数据库,要注意下面两点
注意1:在可被访问的表中将命名为_id的column定义为INTEGER PRIMARY KEY AUTOINCREMENT
注意2:在装入表后建立合适的索引并且使用ANALYZE命令
Step#2 创建content URI
例子:
为stocksymbols表创建两个URI, 第一个用于获取所有数据,第二个用于获取一行数据
content:// com.example.andapp1. provider/stocksymbols
content:// com.example.andapp1. provider/ stocksymbols/#
Step#3 创建MIME类型
例子:
与上面的URI相对应,一个表格有两个MIME类型
vnd.android.cursor.dir/ stocksymbols
vnd.android.cursor.item/stocksymbols
与之对应的,在contract class里定义对应的整数,该整数可被方法getType()用于返回MIME类型
private static final int STOCKSYMBOLS_ALL = 1; // All stock symbols
private static final int STOCKSYMBOLS_ID = 2; // All stock symbols specific ID
Step#4 创建contract class
为每个表创建一个contract class. 这个class包含与表格column名对应的public static final 变量,content URI 和MIME类型。通常,contract class实现BaseColumns 接口
例子:
public final class ContractClassSTOCKSYMBOLS implements BaseColumns {
// This class should not be instantiated
private ContractClassSTOCKSYMBOLS() { }
// Define CONTENT_AUTHORITY
public static final String CONTENT_AUTHORITY = "com.example.andapp1. provider";
// Define BASE_CONTENT_URI as a Uri object
public static final Uri BASE_CONTENT_URI = Uri.parse(" content://" + CONTENT_AUTHORITY);
/ Define CONTENT_URI as a Uri object
public static final Uri CONTENT_URI = Uri.parse(" content://" + CONTENT_AUTHORITY + "/ stocksymbols");
// Define CONTENT_TYPE to represent the MIME Type for all rows in the table
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/ stocksymbols";
// Define CONTENT_ITEM_TYPE to represent the MIME Type for an individual row in the table
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/ stocksymbols";
// TABLE COLUMN DEFINITIONS:
// _ID: Note that BaseColumns automatically brings in _ID set to "_id",
// so we don't have to do that!
// STOCK_SYMBOL
public static final String STOCKSYMBOL = "STOCK_SYMBOL";
...
}
Step#5 创建content provider
创建一个继承自ContentProvider的类. 在这个类里面override delete(), insert(), update(), query(), getType() 和onCreate()方法 .
例子:
// All stock symbols
private static final int STOCKSYMBOLS_ALL = 1;
// All stock symbols specific _ID
private static final int STOCKSYMBOLS_ID = 2;
// PACKAGE_NAME
private static final String PACKAGE_NAME = "com.example.andapp1";
// CONTENT_AUTHORITY
private static final String CONTENT_AUTHORITY = "com.example.andapp1. provider";
// UriMatcher used to compare requested content URI with those that are
// allowed by this content provider.
UriMatcher umStock = new UriMatcher( UriMatcher.NO_MATCH);
@Override
public String getType( Uri uri) {
String sType = "";
switch( umStock.match( uri)) {
case STOCKSYMBOLS_ALL:
sType =" vnd.android.cursor.dir/ stocksymbol";
break;
case STOCKSYMBOLS_ID:
sType =" vnd.android.cursor.item/ stocksymbol/#";
break;
default:
sType = null;
break;
}
return sType;
}
public boolean onCreate() {
// Add content URIs to the UriMatcher object
umStock.addURI( CONTENT_AUTHORITY, "stocksymbols", STOCKSYMBOLS_ALL);
umStock.addURI( CONTENT_AUTHORITY, "stocksymbols/#", STOCKSYMBOLS_ID);
// Open the database. YOU MAY NEED TO MODIFY THE FIRST PARAMETER!!
oSQLiteDB = SQLiteDatabase.openDatabase( "/ data/ data/" + PACKAGE_NAME + "/" + DATABASE_NAME, null, SQLiteDatabase.OPEN_READONLY);
return true;
}
public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
String sSQL = "";
String sID = "";
Cursor oCSR = null;
switch( umStock.match( uri)) {
case STOCKSYMBOLS_ALL:
sSQL = "SELECT STOCK_SYMBOL, COMPANY_NAME FROM STOCK_SYMBOLS";
if (selection != null) {
sSQL + = " WHERE " + selection;
}
oCSR = oSQLiteDB.rawQuery( sSQL, null);
break;
case STOCKSYMBOLS_ID:
sID = uri.getLastPathSegment();
sSQL = "SELECT STOCK_SYMBOL, COMPANY_NAME FROM STOCK_SYMBOLS WHERE _ID = " + sID;
oCSR = oSQLiteDB.rawQuery( sSQL, null);
break;
}
// resolver is notified!!
if (oCSR != null) {
oCSR.setNotificationUri( getContext(). getContentResolver(), uri);
}
return oCSR;
}
...
}
注意:上述例子中query实现使用了SQL statement和rawQuery方法,这种方式存在潜在的SQL安全问题。例如sID可以包含一些恶意的代码从而造成对数据库的损害。Android推荐使用db.insert(), update()和SQLiteQueryBuilder的query()方法进行这些数据库操作。
Step#6 创建权限
权限一般定义:
< package-name>.provider.permission.<permission>
例子:
com.example.andapp1. provider.permission.READ_PROVIDER
Step#7 更新AndroidManifest.xml, 加上<provider>tag
1. 在<uses-sdk>和<application>tag间加入permission定义
< permission android:description ="@ string/ perm_desc" android:label =" Equity Database Read-Only" android:name = "com.example.andapp1.provider.permission.READ_PROVIDER" android:protectionLevel =" normal"/ >
2. 在< application > tag内加入下面provider 定义
< provider android:name =". MyContentProviderClass" android:label =" Equity Database Content Provider" android:authorities =" com.example.andapp1. provider" android:readPermission = "com.example.andapp1. provider.permission.READ_PROVIDER" android:syncable =" false"/ >
- 使用Content Resolver的步骤
第一步:编译安装包含Content Provider的应用
第二步:准备好contract class,最好是打在一个lib里
第三步:创建test 应用,在AndroidManifest的<uses-sdk>和<application>之间加入要使用的访问权限
< uses-permission android:name =" com.example.andapp1. provider.permission.READ_PROVIDER"/ >
第四步:使用Content Resolver访问content provider提供的数据例子:
// content resolver code goes here ContentResolver oCR_EQDB = getContentResolver(); // Create a cursor to the data Cursor oCSR = oCR_EQDB.query(ContractClassSTOCKSYMBOLS.CONTENT_URI, new String[] {ContractClassSTOCKSYMBOLS.STOCKSYMBOL, ContractClassSTOCKSYMBOLS.COMPANYNAME}, null, null, null); // Show toasts for each of the entries if (oCSR.getCount() > 0) { while( oCSR.moveToNext()) { sStockSymbol = oCSR.getString( oCSR.getColumnIndex( ContractClassSTOCKSYMBOLS.STOCKSYMBOL)); sStockName = oCSR.getString( oCSR.getColumnIndex( ContractClassSTOCKSYMBOLS.COMPANYNAME)); sStock = sStockSymbol + "/" + sStockName; toast = Toast.makeText( getApplicationContext(), sStock, Toast.LENGTH_SHORT); toast.show(); } }