文章目录
一、简介
ContentProvider
管理对结构化数据集的访问。它们封装数据并通过ContentResolver
接口将其提供给应用程序,并提供用于定义数据安全性的机制。只有在需要在多个应用程序之间共享数据
时,才需要内容提供程序。例如,联系人数据由多个应用程序使用,必须存储在内容提供程序中。如果不需要在多个应用程序之间共享数据,可以通过SQLiteDatabase直接使用数据库。
内容提供程序以一个或多个表(与在关系型数据库中找到的表类似)的形式将数据呈现给外部应用。
二、使用自定义ContentProvider
以《第一行代码》提供示例代码示范:
1. 先有个数据库
/**
* 创建数据库辅助类
*/
public class MyDatabaseHelper extends SQLiteOpenHelper {
// 创建 Book 表的 SQL
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)";
// 创建 Category 表的 SQL
public static final String CREATE_CATEGORY = "create table Category ("
+ "id integer primary key autoincrement, "
+ "category_name text, "
+ "category_code integer)";
private Context mContext;
/**
* 创建DBHelper
*/
public MyDatabaseHelper(Context context, String name,SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
/**
* 创建数据库和表
* 当helper.getxxxDatabase()时会检测数据库不存在,回调此方法
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
}
/**
* 数据库升级
* 调用此helper的构造方法创建helper时,version增大就会回调此方法
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}
2. 再来个内容提供者ContentProvider,提供数据
/**
* 创建内容提供者
*/
public class DatabaseProvider extends ContentProvider {
/** 当URI匹配时返回的code */
public static final int BOOK_DIR = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR = 2;
public static final int CATEGORY_ITEM = 3;
/** 区分程序,一般是包名 */
public static final String AUTHORITY = "com.example.databasetest.provider";
/** URI匹配器 */
private static UriMatcher uriMatcher;
/** DBHelper */
private MyDatabaseHelper dbHelper;
/** 优先于构造方法,创建UriMatcher,添加期望匹配的内容URI */
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
// 通配符 *:表示匹配任意长度的任意字符 #:表示匹配任意长度的数字
uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
// 该URI表示可以匹配category表中的任意一行数据
uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
/**
* 创建ContentProvider
*/
@Override
public boolean onCreate() {
// 同时创建DBHelper , 指定数据库名称为 BookStore.db,数据库版本为2
dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
return true;
}
/**
* 查询数据
* 必须重写,只有当ContentResolver尝试获取该程序数据时,才会调用
* @param uri 指定某个应用下的某张表
* @param projection 指定查询的列名
* @param selection 指定where的约束条件
* @param selectionArgs 为where中的占位符传递具体值
* @param sortOrder 查询结果排序方式
* @return
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// 创建或打开数据库
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
// 根据ContentResolver传入的URI,匹配出要访问的内容
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = db.query("Book", projection, "id = ?", new String[] { bookId }, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("Category", projection, "id = ?", new String[] { categoryId }, null, null, sortOrder);
break;
default:
break;
}
return cursor;
}
/**
* 插入数据
* 必须重写
* @param uri
* @param values 插入的数据
* @return
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
// 添加数据
SQLiteDatabase db = dbHelper.getWritableDatabase();
Uri uriReturn = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("Book", null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("Category", null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId);
break;
default:
break;
}
return uriReturn;
}
/**
* 更新数据
* 必须重写
* @param uri
* @param values 需要更新的新数据
* @param selection 指定where的约束条件
* @param selectionArgs 为where中的占位符传递具体值
* @return 受影响的行数
*/
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// 更新数据
SQLiteDatabase db = dbHelper.getWritableDatabase();
int updatedRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
updatedRows = db.update("Book", values, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updatedRows = db.update("Book", values, "id = ?", new String[] { bookId });
break;
case CATEGORY_DIR:
updatedRows = db.update("Category", values, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updatedRows = db.update("Category", values, "id = ?", new String[] { categoryId });
break;
default:
break;
}
return updatedRows;
}
/**
* 删除数据
* 必须重写
* @param uri
* @param selection
* @param selectionArgs
* @return 删除的行数
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// 删除数据
SQLiteDatabase db = dbHelper.getWritableDatabase();
int deletedRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
deletedRows = db.delete("Book", selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deletedRows = db.delete("Book", "id = ?", new String[] { bookId });
break;
case CATEGORY_DIR:
deletedRows = db.delete("Category", selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deletedRows = db.delete("Category", "id = ?", new String[] { categoryId });
break;
default:
break;
}
return deletedRows;
}
/**
* 根据传入的URI返回对应的 MIME TYPE
* 必须重写
* @param uri
* @return
*/
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
// 表的 MIME 类型由3部分组成:
// 1: 必须以vnd开头
// 2: URI以路径结尾,衔接android.cursor.item/; URI以id结尾,衔接android.cursor.item/
// 3: vnd.<name>.<type> 。 <name> 值应具有全局唯一性,<type> 值应在对应的 URI 模式中具有唯一性。
return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category";
}
return null;
}
}
// 必须在清单文件注册,authorities指定DatabaseProvider的URI的AUTHORITY 部分
<provider
android:name=".DatabaseProvider"
android:authorities="com.example.databasetest.provider"
android:enabled="true"
android:exported="true" />
3. 最后上ContentResolver,连接ContentProviderid,通过SQLiteOpenHelper,操作SQLiteDatabase
ContentProviderid是为了应用间数据共享,所以,我们在新建一个应用,在其中使用ContentResolver,共享上面那个应用中的ContentResolver提供的数据
public class ContentProviderActivity extends AppCompatActivity {
private String newId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content_provider);
// 插入数据
Button addData = (Button) findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 往Book表中插入数据
Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
// 封装要插入的数据
ContentValues values = new ContentValues();
values.put("name", "A Clash of Kings");
values.put("author", "George Martin");
values.put("pages", 1040);
values.put("price", 55.55);
// 获取ContentResolver,插入数据,最终调用的事ContentProvider的insert,插入成功后返回这条新数据的URI
Uri newUri = getContentResolver().insert(uri, values);
// 获取这条新数据在表中的id
newId = newUri.getPathSegments().get(1);
}
});
// 查询数据
Button queryData = (Button) findViewById(R.id.query_data);
queryData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 查询Book表中的所有数据
Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String name = cursor.getString(cursor. getColumnIndex("name"));
String author = cursor.getString(cursor. getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex ("pages"));
double price = cursor.getDouble(cursor. getColumnIndex("price"));
Log.d("ContentProviderActivity", "book name is " + name);
Log.d("ContentProviderActivity", "book author is " + author);
Log.d("ContentProviderActivity", "book pages is " + pages);
Log.d("ContentProviderActivity", "book price is " + price);
}
// 记得关闭cursor,释放资源,避免内存泄漏
cursor.close();
}
}
});
// 更新数据
Button updateData = (Button) findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 更新Book表中id为newId的那条数据
Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
// 新数据
ContentValues values = new ContentValues();
values.put("name", "A Storm of Swords");
values.put("pages", 1216);
values.put("price", 24.05);
getContentResolver().update(uri, values, null, null);
}
});
// 删除数据
Button deleteData = (Button) findViewById(R.id.delete_data);
deleteData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 删除Book表中id未newId的那条数据
Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
getContentResolver().delete(uri, null, null);
}
});
}
}
三、使用系统ContentProvider
1. 读取联系人
// 查询联系人数据
Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
// 获取联系人姓名
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
// 获取联系人手机号
String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
}
}
<!--获取读取联系人权限,危险权限-->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
2. 读取短信
Uri uri = Uri.parse("content://sms/");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
while (cursor.moveToNext()) {
// 短信内容
String body = cursor.getString(cursor.getColumnIndex("body"));
// 接收人
String address = cursor.getString(cursor.getColumnIndex("address"));
}
<!--获取读取短信权限,危险权限-->
<uses-permission android:name="android.permission.READ_SMS"/>
3. 读取音乐
Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()) {
String music_data = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));//歌曲路径
String musictitle = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));//歌曲名字
String musicartist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));//歌曲歌手
String musicalbum = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));//歌曲专辑
String music_size = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE));//歌曲大小
String musicduration = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));//歌曲时长
}
<!--获取手机存储卡权限,危险权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
4. 读取图片
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()) {
// 获取图片的名称
String name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
// 获取图片的生成日期
byte[] data = cursor.getBlob(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
// 获取图片的详细信息
String desc = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DESCRIPTION));
}
<!--获取手机存储卡权限,危险权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
个人总结,水平有限,如果有错误,希望大家能给留言指正!如果对您有所帮助,可以帮忙点个赞!如果转载,希望可以标明文章出处!最后,非常感谢您的阅读!