概念
内容提供者管理对中央数据存储区(结构化数据集)的访问。通常是其他应用通过内容提供者对应用的数据访问。通常情况下如果不需要与其他应用共享数据,就不需要使用内容提供者。
内容提供者通过一个或多个表(对sqlite的封装)的形式将数据呈现给外部应用。对数据的增删改查与sqlite数据库基本一致。
URl
用于标识数据的URl,包括内容提供者的名字(唯一标识、授权)的一个指向表的名称(路径)。外部应用通过URL来访问指定的数据。如:
content://user_dictionary/words
其中,user_dictionary 字符串是提供程序的授权,words 字符串是表的路径。
读写访问权限
内容提供程序提供结构化存储机制,可以将内容限制为仅供自己的应用访问,也可以将内容导出以供其他应用访问。如果您不打算向其他应用授予访问您的 ContentProvider 的权限,请在应用清单中将其标记为 android:exported=false;要允许其他应用访问存储的数据,请将 android:exported 属性设置为 “true”。
<provider
android:name=".provider.FengfanckyProvider"
android:authorities="cn.math.cky"
android:exported="true" />
在创建要导出以供其他应用使用的 ContentProvider 时,您可以在清单中指定允许读取和写入的单一权限,也可以针对读取和写入操作分别指定权限。如下:
1.在application的同级标签中申请一个自定义权限
<permission
android:name="cn.math.cky.provider.read"
android:label="provider pomission"
android:protectionLevel="normal" />
<permission
android:name="cn.math.cky.provider.write"
android:label="provider pomission"
android:protectionLevel="normal" />
<permission
android:name="cn.math.cky.provider.permission"
android:label="provider pomission"
android:protectionLevel="normal" />
2.将上述申请的权限赋值给provider的readPermission或writePermission
<provider
android:name=".provider.FengfanckyProvider"
android:authorities="cn.math.cky"
android:readPermission="cn.math.cky.provider.read"
android:writePermission="cn.math.cky.provider.write"/>
或
<provider
android:name=".provider.FengfanckyProvider"
android:authorities="cn.math.cky"
android:permission="cn.math.cky.provider.permission"/>
3.在第三方应用中根据需求使用uses-permission来声明使用对应的权限
<uses-permission android:name="cn.math.cky.provider.read"/>
或
<uses-permission android:name="cn.math.cky.provider.write"/>
或
<uses-permission android:name="cn.math.cky.provider.permission"/>
provider几个的属性:
- exported:其他应用是否可以访问该ContentProvider。
- readPermission: ContentProvider读取权限。
- writePermission: ContentProvider写入权限。
- permission: 一次性设置ContentProvider的读写权限,如果同时设置了readPermission、writePermission,优先级低于readPermission、writePermission。
- grantUriPermissions:并使用用来启动组件的 Intent 对象中的 FLAG_GRANT_READ_URI_PERMISSION 和 FLAG_GRANT_WRITE_URI_PERMISSION 标记。
如下:
<provider
android:name=".provider.FengfanckyProvider"
android:authorities="cn.math.cky"
android:readPermission="cn.math.cky.provider.read"
android:grantUriPermissions="true"/>
可以通过Intent将权限传递给第三方应用:
Intent intent = new Intent(this,ReadProvider.class);
intent.setClassName("xx.xxx.xxx","xx.xxx.xxx.MainActivity");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //传递权限
startActivity(intent);
如果将provider的属性android:grantUriPermissions设置为false,则不允许通过接受传递的权限方式。
创建内容提供者
创建步骤
- 设计数据存储原始结构。主要有两种:文件数据、结构化数据(SQLite数据库)。
- 定义 ContentProvider 类及其所需方法的具体实现。
- 定义提供程序的授权字符串、其内容 URI 以及列名称。
- 其他。
1.创建数据存储(SQLite)
定义一个常量类,URI、表名、列名等
public class Contracts {
protected static final String CONTENT_AUTHORITY = "cky.provider";
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
public static final class HistoryEntry implements BaseColumns {
protected static final String TABLE_NAME = "history";
public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(TABLE_NAME).build();
protected static Uri buildUri(long id) {
return ContentUris.withAppendedId(CONTENT_URI, id);
}
public static final String COLUMN_ID="contentID";
public static final String COLUMN_NAME="name";
public static final String COLUMN_PICURL="picUrl";
public static final String COLUMN_PACKAGENAME="packageName";
}
}
创建SQLite数据库,继承至SQLiteOpenHelper。创建数据库,创建表,更新表
public class DBHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION =1;
private static final String DATABASE_NAME = "cky_test.db";
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
final String SQL_CREATE_HISTORY_TABLE = "CREATE TABLE "
+ Contracts.HistoryEntry.TABLE_NAME + "( "
+ Contracts.HistoryEntry.COLUMN_ID + " INTEGER PRIMARY KEY, "
+ Contracts.HistoryEntry.COLUMN_NAME + " CHAR(50), "
+ Contracts.HistoryEntry.COLUMN_PICURL + " CHAR(50), "
+ Contracts.HistoryEntry.COLUMN_PACKAGENAME + " CHAR(50) ,"
+ Contracts.HistoryEntry.COLUMN_TYPE + " CHAR(50) ,"
+ Contracts.HistoryEntry.COLUMN_FILMPATH + " CHAR(50) ,"
+ Contracts.HistoryEntry.COLUMN_MOIVEPATH + " CHAR(50) ,"
+ Contracts.HistoryEntry.COLUMN_UPDATETIME + " INTEGER );";
db.execSQL(SQL_CREATE_HISTORY_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + Contracts.HistoryEntry.TABLE_NAME);
onCreate(db);
}
}
2.创建ContentProvider以及所需方法实现
使用UriMatcher,匹配不同的uri来访问不同的表
private final static int HISTORY = 100;
private final static int COLLECT = 200;
static UriMatcher buildUriMatcher() {
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = Contracts.CONTENT_AUTHORITY;
matcher.addURI(authority, Contracts.HistoryEntry.TABLE_NAME, HISTORY);
matcher.addURI(authority, Contracts.CollectEntry.TABLE_NAME, COLLECT);
return matcher;
}
继承ContentProvider,实现onCreate(),getType(),query(),insert(),delete(),update()方法。
public class UserInfoProvider extends ContentProvider {
private DBHelper mOpenHelper;
@Override
public boolean onCreate() {
mOpenHelper = new DBHelper(getContext());
return true;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor cursor = null;
switch ( buildUriMatcher().match(uri)) {
case HISTORY:
cursor = db.query(Contracts.HistoryEntry.TABLE_NAME, projection, selection, selectionArgs, sortOrder, null, null);
break;
case COLLECT:
cursor = db.query(Contracts.CollectEntry.TABLE_NAME, projection, selection, selectionArgs, sortOrder, null, null);
break;
}
return cursor;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Uri returnUri;
long _id;
switch ( buildUriMatcher().match(uri)) {
case HISTORY:
_id = db.insert(Contracts.HistoryEntry.TABLE_NAME, null, values);
if ( _id > 0 )
returnUri = Contracts.HistoryEntry.buildUri(_id);
else
throw new android.database.SQLException("Failed to insert row into " + uri);
break;
case COLLECT:
_id = db.insert(Contracts.CollectEntry.TABLE_NAME, null, values);
if ( _id > 0 )
returnUri = Contracts.CollectEntry.buildUri(_id);
else
throw new android.database.SQLException("Failed to insert row into " + uri);
break;
default:
throw new android.database.SQLException("Unknown uri: " + uri);
}
return returnUri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
int num=0;
switch ( buildUriMatcher().match(uri)) {
case HISTORY:
num=db.delete(Contracts.HistoryEntry.TABLE_NAME,selection,selectionArgs);
break;
case COLLECT:
num=db.delete(Contracts.CollectEntry.TABLE_NAME,selection,selectionArgs);
break;
}
return num;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
int num=0;
switch ( buildUriMatcher().match(uri)) {
case HISTORY:
num=db.update(Contracts.HistoryEntry.TABLE_NAME,values,selection,null);
break;
case COLLECT:
num=db.update(Contracts.CollectEntry.TABLE_NAME,values,selection,null);
break;
}
return num;
}
}
数据的增删改查
应用从具有 ContentResolver 客户端对象的内容提供程序访问数据。 此对象具有调用提供程序对象(ContentProvider 的某个具体子类的实例)中同名方法的方法。 ContentResolver 方法可提供持续存储的基本“CRUD”(创建、检索、更新和删除)功能。
1.数据查询query()
// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // The content URI of the words table
mProjection, // The columns to return for each row
mSelectionClause // Selection criteria
mSelectionArgs, // Selection criteria
mSortOrder); // The sort order for the returned rows
Query() 与 SQL 查询对比:
获取查询结果:
Cursor cursor=getContentResolver().query(Contracts.HistoryEntry.CONTENT_URI,null,null,null,null,null);
if (cursor!=null){
while (cursor.moveToNext()){
String contentID=cursor.getString(cursor.getColumnIndex(Contracts.HistoryEntry.COLUMN_ID));
String name=cursor.getString(cursor.getColumnIndex(Contracts.HistoryEntry.COLUMN_NAME));
String pic_url=cursor.getString(cursor.getColumnIndex(Contracts.HistoryEntry.COLUMN_PICURL));
}
}
2.数据插入
ContentValues contentValues=new ContentValues();
contentValues.put(Contracts.HistoryEntry.COLUMN_ID,"12233332");
contentValues.put(Contracts.HistoryEntry.COLUMN_NAME,"hello");
contentValues.put(Contracts.HistoryEntry.COLUMN_PICURL,"http://xx.xxx.xx");
getContentResolver().insert(Contracts.HistoryEntry.CONTENT_URI,contentValues);
3.数据更新
ContentValues contentValues=new ContentValues();
contentValues.put(Contracts.HistoryEntry.COLUMN_NAME,"hi");
getContentResolver().update(
Contracts.HistoryEntry.CONTENT_URI,
Contracts.HistoryEntry.COLUMN_ID+"=12233332",
null);
4.数据删除
删除行与检索行数据类似:为要删除的行指定选择条件,客户端方法会返回已删除的行数。 以下代码段会删除应用 ID 与“用户”匹配的行。该方法会返回已删除的行数。
getContentResolver().delete(
Contracts.HistoryEntry.CONTENT_URI,
Contracts.HistoryEntry.COLUMN_ID+"=12233332",
null);