使用内容提供器(Content Provider),可以实现不同应用程序建的数据共享功能。
内容提供器提供了一套完整的机制,允许一个程序访问另一个程序中的数据同时内容提供器可以选择对那一部分数据进行共享进而保证被访数据的安全性。目前内容提供器是Android实现跨程序共享数据的标准方式。
内容提供器的用法有两种:
- 使用现有的内容提供器来读取和操作相应程序中的数据。
- 创建自己的内容提供器,给程序的数据提供外部访问接口。
1. 访问其他程序中的数据
所有的应用程序如果想要访问内容提供器共享的数据,就一定要借助ContentResolve类
1.1 ContentResolver的基本用法
- ContentResolver类可以通过Context中的getContentResolver()方法获取该类的实例,该类提供了一些列的方法用于对数据进行CRUD(增删查改)操作
- insert():用于添加数据,
- update():用于更新数据,
- delete():用于删除数据,
- query():用于查询数据。
ContentResolver的CRUD操作方法与SQLite类似,但是ContentResolver把SQLite的第一个参数(表名)替换成一个URI参数,Uri参数指定了要访问的程序和该程序的某个表,由权限和路径组成。
权限:是用于对不同程序做区分的(要访问的程序)。为了避免冲突,一般会采用程序包名作命名。
路径:则是用于对同一应用程序中的不同表做区分的(要访问的表),通常都会添加到权限后面
以下是内容URI的标准写法格式,
content://com.example.app.provider/table1
content://com.example.app.provider/table2
- 在得到了URI字符串之后,还需调用Uri.parse方法将它解析成Uri对象,才可作为参数传入
Uri = urii = Uri.parse("content://com.example.app.provider/table1");
1.2获取某个程序中某个表的数据
使用querry()方法可以实现查询功能
- querry()方法对应的参数如下图:
使用示例:
Cursor cursor = getContentResolver().querry(uri,
projection,
selection,
selectionArgs,
sortOrder);
查询完成后返回的是一个Cursor对象,这时我们就可以从Cursor对象中读取查询到数据
if (cursor != null) {
while (cursor.moveToNext()) {
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getIn(cursor.getColumnIndex("column2"));
}
cursor.close();
}
1.3向某个程序某个表添加数据
ContentValues values = new ContentValues();
values.put("column1", "text");
values.put("column2", 1);
//获取ContentResolver对象,并调用insert()方法
getContentResolver().insert(uri, values);
1.4更新某个程序某个表的数据
ContentValues values = new ContentValues();
values.put("column1", "");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[] {"text", "1"});
1.5 删除某个程序某个表的数据
getContentResolver().delete(uri, "column2 = ?", new String[] {"1"});
2. 程序间共享实例,读取系统联系人
-
第一步修改布局文件
添加一个Listview来显示从系统获取的联系人
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/contacts_view"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</LinearLayout>
- 第二步:在activity中获取联系人数据
public class MainActivity extends AppCompatActivity {
ListView contactsView;
ArrayAdapter<String> adapter;
List<String> contactsList = new ArrayList<String>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
contactsView = (ListView) findViewById(R.id.contacts_view);
adapter = new ArrayAdapter<String>(this, R.layout.support_simple_spinner_dropdown_item, contactsList);
contactsView.setAdapter(adapter);
readContacts();
}
private void readContacts() {
Cursor cursor = null;
try {
//查询联系人数据
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, 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));
contactsList.add(displayName + "\n" + number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
- 第三步:在Manifest中声明读取联系人的权限
<uses-permission android:name="android.permission.READ_CONTACTS"/>
</manifest>
- 运行结果
3.自定义内容提供器
- 第一步:新建一个类继承自ContentProvider类
public class MyContentProvider extends ContentProvider {
///初始化内容提供器时调用
//通常会在这里完成对数据库的创建和升级等操作
//返回true表示初始化成功,false为失败
//注:只有当ContentResolver尝试访问程序的数据时,内容提供器才会被初始化。
@Override
public boolean onCreate() {
return false;
}
///从内容提供器中查询数据
//uri : 确定查询哪张表
//projection : 确定查询哪些列
//selection 和 selectionArgs : 约束查询哪些行
//sortOrder : 用于对结果进行排序
//return : Cursor对象,存放查询结果
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
///向内容提供器中添加一条数据
//uri :确定添加到哪张表
//values :要添加的数据,保存在ContentValues中
//return : 添加完成后,返回一个用于表示这条新纪录的URI
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
///更新内容提供器中已有的数据
//uri : 确定更新哪一张表的数据
//values : 新的数据,保存在ContentValues中
//selection 和 selectionArgs :用于约束更新哪些行。
//return :受影响的行数
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
///从内容提供器中删除数据
//uri : 确定要删除哪一张表的数据
//selection 和 selectionArgs : 用于约束删除哪些行
//return :删除的行数
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
///根据传入的URI来返回相应的MIME类型
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
}
- 第二步:对传入的Uri参数进行解析
URI的写法格式有两种:
- 以路径结尾的,表示期望访问该表中所有的数据:
content://com.example.app.provider/table1
- 以id结尾的,表示期望访问该表中拥有相应id的数据:
content://com.example.app.provider/table1/1
此外,我们还可以使用通配符的方式来匹配这种格式的内容URI。- *:表示匹配任意长度的任意字符
:表示匹配任意长度的数字
例如:
一个能够匹配任意表的内容URI格式可以写成:content://com.example.app.provider/*
*
一个能够匹配table表中任一一行数据的内容URI格式可以写成:content://com.example.app.provider/table1/#
- 之后再借助UriMatcher这个类就可以轻松实现匹配内容URI的功能
Urimatcher提供了一个addURI()方法,调用该方法需把权限、路径和一个自定义的代码穿进去。然后通过调用UriMatcher的match()方法时,将外界传入的uri对象传入给该方法,返回得到对应的自定义代码,从而判断出外界期望访问的是哪张表的数据了。
public class MyContentProvider extends ContentProvider {
public static final int TABLE1_DIR = 0;
public static final int TABLE1_ITEM = 1;
public static final int TABLE2_DIR = 2;
public static final int TABLE2_ITEM = 3;
private static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
uriMatcher.addURI("com.example.app.provider", "table1/#", TABLE1_ITEM);
uriMatcher.addURI("com.example.app.provider", "table2", TABLE2_DIR);
uriMatcher.addURI("com.example.app.provider", "table2/#", TABLE2_ITEM);
}
///初始化内容提供器时调用
//通常会在这里完成对数据库的创建和升级等操作
//返回true表示初始化成功,false为失败
//注:只有当ContentResolver尝试访问程序的数据时,内容提供器才会被初始化。
@Override
public boolean onCreate() {
return false;
}
///从内容提供器中查询数据
//uri : 确定查询哪张表
//projection : 确定查询哪些列
//selection 和 selectionArgs : 约束查询哪些行
//sortOrder : 用于对结果进行排序
//return : Cursor对象,存放查询结果
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
//查询table1表中的所有数据
break;
case TABLE1_ITEM:
//查询table1表中的单条数据
break;
case TABLE2_DIR:
//查询table2表中的所有数据
break;
case TABLE2_ITEM:
//查询table2表中的单条数据
break;
}
return null;
}
}
- 第三步:实现getType()方法中的逻辑
getType()是所有内容提供器都必须提供的一个方法,用于获取Uri对象所对应的MIME类型,MIME类型字符串主要包含以下三个部分:
- 必须以vnd开头
- 如果内容URI以路径结尾,则后接
android.cursor.dir/
,如果内容URI以id结尾,则后接android.cursor.item/
- 最后接上
vnd.<authority>.<path>
例如:
- content://com.example.app.provider.table1的URI随对应的MIME类型可以写成:
vnd.android.cursor.dir/vnd.com.example.app.provider.table1
- 对于
content://com.example.app.provider/table1/1
这个内容URI所对应的MIME类型可以写成:
van.android.cursor.item/vnd. com.example.app.provider.table1
///根据传入的URI来返回相应的MIME类型
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
case TABLE1_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
case TABLE2_DIR:
return "vnd.android.cursor.dir/";
case TABLE2_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
default:
break;
}
return null;
}
实现跨程序数据共享
- 第一步:在需要共享数据出去的程序自定义一个内容提供器。
public class DatabaseProvider extends ContentProvider {
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;
private static final String AUTHORITY = "com.example.sharedpreferencestest.provider";
private static UriMatcher uriMatcher;
private MyDatabaseHelper dbHelper;
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);
uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
@Override
public boolean onCreate() {
dbHelper = new MyDatabaseHelper(getContext(), "BookStore2.db", null, 2);
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
//查询数据
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
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;
}
return cursor;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
//添加数据
SQLiteDatabase db = dbHelper.getWritableDatabase();
Uri uriReturn = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("Book2", null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/book2/" + 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;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
//更新数据
SQLiteDatabase db = dbHelper.getWritableDatabase();
int updateRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
updateRows = db.update("Book", values, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updateRows = db.update("Book", values, "id = ?", new String[] { bookId });
break;
case CATEGORY_DIR:
updateRows = db.update("Category", values, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updateRows = db.update("Category", values, "id = ?", new String[] { categoryId });
break;
default:
break;
}
return updateRows;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
//删除数据
SQLiteDatabase db = dbHelper.getWritableDatabase();
int deleteRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
deleteRows = db.delete("Book", selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deleteRows = db.delete("Book", "id = ?", new String[] { bookId });
break;
case CATEGORY_DIR:
deleteRows = db.delete("Category", selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deleteRows = db.delete("Category", "id = ?", new String[] { categoryId });
break;
}
return deleteRows;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
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;
}
}
2.第二步:在配置文件注册内容提供器权限
</activity>
<provider
android:authorities="com.example.sharedpreferencestest.provider"
android:name="com.example.anwser_mac.sharedpreferencestest.DatabaseProvider"
android:exported="true">
</provider>
</application>
3.第三步:在需要获取数据的程序实现获取数据
public class MainActivity extends AppCompatActivity {
private String newId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
Button addData = (Button) findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//添加数据
Uri uri = Uri.parse("content://com.example.sharedpreferencestest.provider/book2");
ContentValues values = new ContentValues();
values.put("name", "A Clash of Kings");
values.put("author", "George Martin");
values.put("pages", 1020);
values.put("price", 999);
Uri newUri = getContentResolver().insert(uri, values);
newId = newUri.getPathSegments().get(1);
}
});
Button queryData = (Button) findViewById(R.id.query_data);
queryData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//查询数据
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("MainActivity", "book name is " + name);
Log.d("MainActivity", "book author is " + author);
Log.d("MainActivity", "book pages is" + pages);
Log.d("MainActivity", "book price is" + price);
}
cursor.close();
}
}
});
Button updateData = (Button) findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//更新数据
Uri uri = Uri.parse("content://example.databasetest.provider/book/" + newId);
ContentValues values = new ContentValues();
values.put("name", "A storm of Swords");
values.put("pages", 1000);
values.put("price", 10.21);
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) {
//删除数据
Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
getContentResolver().delete(uri, null, null);
}
});
}
}