ContentProvider:在Android中实现进程间数据共享
目录
一,ContentProvider
二,Uri和UriMatcher工具类
1,Uri
2,UriMatcher
三,自定义ContentProvider
1,准备数据源
2,创建ContentProvider子类
3,在Manifest文件中注册ContentProvider
四,使用ContentResolver访问数据
五,获取系统中CP的数据
1,获取通话记录
2,管理多媒体内容
一,ContentProvider
ContentProvider内容提供者,是一种实现不同应用间共享数据的标准api,当应用想要提供数据时,就提供ContentProvider,其他应用通过ContentResolver来接收数据;
ContentProvider以Uri的形式对外提供数据,其他应用使用ContentResolver通过Uri来访问数据;
ContentProvider是单例模式的,多个ContentResolver请求数据时,是委托给同一个CP对象来操作的;
二,Uri和UriMatcher工具类
1,Uri
Uri的结构和网站URL命名规则类似:
Uri举例:content://org.edu.provider/words
- content://:这部分是Android固定的
- org.edu.provider:这部分是ContentProvider中的authority,结构一般为包名.provider
- words:资源部分,根据资源不同这部分不同
通过Uri提供的静态方法parse()来实现将字符串转换为Uri:
- Uri uri = Uri.parse("content://org.edu.p../words")
2,UriMatcher
帮助ContentProvider操作Uri,进行Uri判断和注册等功能;
(1)UriMatcher通过构造器构造:
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
//UriMatcher.NO_MATCH 表示不匹配任何路径的返回码(返回-1)
(2)注册Uri,只有注册过的Uri才能被访问:
//Uri的authorities
private static String AUTHORITIES = "com.example.contentprovider.ContentProvider.provider";//Uri的标识码,用于标识唯一的资源
private static Integer BOOK_CODE = 0;//注册Uri authorities + 资源名(表名)+ 标识码
matcher.addURI(AUTHORITIES, "tb_books", BOOK_CODE);//最终Uri格式为"content://com.example.contentprovider.ContentProvider.provider/tb_books/0"
(3)通过int match(Uri uri)方法来判断Uri的对应的标识符,找不到返回-1:
private String getTable(Uri uri) {String tableName = "";if(matcher.match(uri) == BOOK_CODE)tableName = "tb_books";return tableName;
}
三,自定义ContentProvider
- 准备数据源:可以是文件存储,Sqlite数据库等数据源;
- 创建ContentProvider:自定义一个类继承ContentProvider,并重写增删改查等方法;
- Manifest文件中注册ContentProvider;
1,准备数据源
(1)以Sqlite数据库为数据源,创建SqliteOpenHelper类:
public class SqliteHelper extends SQLiteOpenHelper {public SqliteHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {super(context, name, factory, version);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("create table if not exists tb_books(" +"id Integer primary key autoincrement, " +"name varchar(20) not null" +");");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
}
2,创建ContentProvider子类
(2)创建ContentProvider,重写增删改查等方法,使用UriMatcher注册Uri:
public class MyContentProvider extends ContentProvider {private SQLiteDatabase bookDB;private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);private static String AUTHORITIES = "com.example.contentprovider.ContentProvider.provider";private static Integer BOOK_CODE = 0;//注册Uripublic MyContentProvider() {matcher.addURI(AUTHORITIES, "tb_books", BOOK_CODE);}@Overridepublic String getType(Uri uri) {return null;}private String getTable(Uri uri) {String tableName = "";if(matcher.match(uri) == BOOK_CODE)tableName = "tb_books";return tableName;}@Overridepublic boolean onCreate() {//获取Sqlite数据库SqliteHelper sqliteHelper = new SqliteHelper(getContext(),"bookDB.db",null,1);bookDB = sqliteHelper.getWritableDatabase();//插入数据ContentValues contentValuesBooks = new ContentValues();contentValuesBooks.put("name", "book1");bookDB.insert("tb_books", null, contentValuesBooks);return true;}@Overridepublic Uri insert(Uri uri, ContentValues values) {String table = getTable(uri);if(table.isEmpty()){return null;}else {long insert = bookDB.insert(table, null, values);if(insert > 0)return uri;else return null;}}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {String table = getTable(uri);if(table.isEmpty()){return -1;}else {int delete = bookDB.delete(table, selection, selectionArgs);return delete;}}@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {String table = getTable(uri);if(table.isEmpty()){return null;}else {Cursor cursor = bookDB.query(table, projection, selection, selectionArgs, null, null,sortOrder);return cursor;}}@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {String table = getTable(uri);if(table.isEmpty()){return -1;}else {int update = bookDB.update(table, values, selection, selectionArgs);return update;}}
}
3,在Manifest文件中注册ContentProvider
四,使用ContentResolver访问数据
(1)通过getContentResolver()方法获取ContentResolver对象:
ContentResolver contentResolver = getContentResolver();
(2)获取Uri:
Uri uri = Uri.parse("content://com.example.contentprovider.ContentProvider.provider/tb_books");
(3)通过ContentResolver调用增删改查等方法:
public class MainActivity extends AppCompatActivity {private ActivityMainBinding binding = null;private ContentResolver contentResolver;private Uri uri;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());contentResolver = getContentResolver();uri = Uri.parse("content://com.example.contentprovider.ContentProvider.provider/tb_books");binding.btnAdd.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {AlertDialogUtil.showDialog(MainActivity.this,"添加","id","书名",new AlertDialogUtil.DialogClickCallback() {@Overridepublic void onClick(DialogInterface dialog, int which, String input1, String input2) {ContentValues contentValues = new ContentValues();contentValues.put("id", Integer.valueOf(input1));contentValues.put("name", input2);Uri insert = contentResolver.insert(uri, contentValues);if (insert != null) {Toast.makeText(MainActivity.this, "插入成功", Toast.LENGTH_SHORT).show();} elseToast.makeText(MainActivity.this, "插入失败", Toast.LENGTH_SHORT).show();}});}});binding.btnDelete.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {AlertDialogUtil.showDialog(MainActivity.this,"删除","id","书名",new AlertDialogUtil.DialogClickCallback() {@Overridepublic void onClick(DialogInterface dialog, int which, String input1, String input2) {String selection = "id = ?";String[] selectionArgs = {input1};int delete = contentResolver.delete(uri, selection, selectionArgs);if(delete > 0){Toast.makeText(MainActivity.this, "删除成功", Toast.LENGTH_SHORT).show();}elseToast.makeText(MainActivity.this, "删除失败", Toast.LENGTH_SHORT).show();}});}});binding.btnUpdate.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {AlertDialogUtil.showDialog(MainActivity.this,"修改","id","书名",new AlertDialogUtil.DialogClickCallback() {@Overridepublic void onClick(DialogInterface dialog, int which, String input1, String input2) {ContentValues contentValues = new ContentValues();contentValues.put("name", input2);String selection = "id = ?";String[] selectionArgs = {input1};int update = contentResolver.update(uri, contentValues, selection, selectionArgs);if(update > 0){Toast.makeText(MainActivity.this, "修改成功", Toast.LENGTH_SHORT).show();}elseToast.makeText(MainActivity.this, "修改失败", Toast.LENGTH_SHORT).show();}});}});binding.btnQuery.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Cursor cursor = contentResolver.query(uri, null, null, null, null);StringBuffer result = new StringBuffer();if(cursor == null){Toast.makeText(MainActivity.this, "查询失败", Toast.LENGTH_SHORT).show();}else{while(cursor.moveToNext()){int indexOfName = cursor.getColumnIndex("name");int indexOfId = cursor.getColumnIndex("id");int id = cursor.getInt(indexOfId);String name = cursor.getString(indexOfName);result.append("id: " + id + ", name: " + name + "\n");}}binding.tvResult.setText(result);}});}
}
五,获取系统中CP的数据
Android系统应用会采用CP(ContentProvider)的形式提供一些开放数据,只要知道相关的Uri。我们就可以直接通过ContentResolver来访问这些数据;
1,获取通话记录
通话记录的Uri为:CallLog.Calls.CONTENT_URI
获取通话记录用到的常量:
- CallLog.Calls._ID: "_id"
- CallLog.Calls.CACHED_NAME:”name”
- CallLog.Calls.NUMBER:”number”
- CallLog.Calls.TYPE:”type” CallLog.Calls.DATE:”date”
- CallLog.Calls.DURATION:”duration”
2,管理多媒体内容
此CP支持的Uri如下:
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
- MediaStore.Audio.Media.INTERNAL_CONTENT_URI
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI
- MediaStore.Images.Media.INTERNAL_CONTENT_URI
- MediaStore.Video.Media.EXTERNAL_CONTENT_URI
- MediaStore.Video.Media.INTERNAL_CONTENT_URI
- Content://sms/outbox 发送箱短信URI
- Content://sms/sent 收信箱短信URI
- Content://sms/draft 草稿箱短信URI