Android开发基础7 - 内容提供器

本篇文章主要介绍以下几个知识点:

  • Android 运行时权限;
  • 内容提供器。

7.1 运行时权限

  在介绍内容提供器之前先来了解了解 Android 运行时权限。

7.1.1 Android 权限机制

  Android 把所有的权限归成两类:普通权限和危险权限。

  • 普通权限
      不会直接威胁到用户的安全和隐私的权限,对于这部分的权限申请,系统会自动帮我们进行授权。
  • 危险权限
      可能触及用户隐私,或对设备安全性造成影响的权限,如获取联系人信息、地理位置等,对于这部分的权限申请,必须由用户手动点击授权才可以,否则程序无法使用相应的功能。

  下面列出了 Android 中所有的危险权限:

  上表中每个危险权限都属于一个权限组,若用户同意授权某个权限名,那么该权限所对应的权限组中的其他权限也会同时跟着被授权。

  注:访问Android官网可以查看完整的权限列表。

7.1.2 在运行程序时申请权限

  接下来通过个例子来介绍如何在运行程序时申请权限。

  此次的例子是通过点击界面上的一个按钮来直接拨打电话。拨打电话时需要声明上节表中的 CALL_PHONE 这个危险权限。

  首先,在 AndroidManifest.xml 文件中声明权限:

1
<uses-permission android:name="android.permission.CALL_PHONE" />

  然后在布局 activity_call_phone.xml 中添加个按钮:

1
2
3
4
5
6
7
8
9
10
11
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<Button
android:id="@+id/btn_call_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击拨打电话"/>

</RelativeLayout>

  接着修改 activity 中代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class CallPhoneActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call_phone);

Button btn_call_phone = (Button) findViewById(R.id.btn_call_phone);
btn_call_phone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
// Intent.ACTION_CALL 是系统内置的打电话动作(而Intent.ACTION_DIAL指打开拨号界面,不需要声明权限)
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}catch (SecurityException e){
e.printStackTrace();
}
}
});
}
}

  在 Android 6.0 前,拨打电话功能的实现很简单,上面代码就把拨打电话功能实现了,在低于 Android 6.0 系统的手机上可以正常运行,但在 Android 6.0 或更高版本系统的手机上运行则无效,会抛出“Permission Denial”的异常。

  接下来,修改 activity 中的代码,修复上面的问题,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class CallPhoneActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call_phone);

Button btn_call_phone = (Button) findViewById(R.id.btn_call_phone);
btn_call_phone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 1. 判断用户是否授权,借助 ContextCompat.checkSelfPermission() 方法
// ContextCompat.checkSelfPermission() 方法接收两参数:context 和权限名
if (ActivityCompat.checkSelfPermission(CallPhoneActivity.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
// 3. 若没授权,则调用 ActivityCompat.requestPermissions()方法来向用户授权
// 其三个参数:Activity实例、String数组(放权限名)、请求码(只要唯一就行了,这里传1)
ActivityCompat.requestPermissions(CallPhoneActivity.this,
new String[]{Manifest.permission.CALL_PHONE}, 1);
// 4. 调用完 requestPermissions()方法后,会弹出一个权限申请对话框,供用户选择,
// 最后回调 onRequestPermissionsResult()方法
}else {
// 2. 若已授权,则直接执行拨打电话的逻辑
call();
}
}
});
}

/**
* 拨打电话方法
*/
private void call() {
try {
// Intent.ACTION_CALL 是系统内置的打电话动作
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}catch (SecurityException e){
e.printStackTrace();
}
}

/**
* 无论用户是否同意权限申请,都会回调此方法
* @param requestCode
* @param permissions
* @param grantResults 授权的结果
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
call();
}else {
ToastUtils.showShort("你拒绝了权限请求");
}
break;

default:
break;
}
}
}

  这样就完成了权限申请的代码编写了,具体过程看代码注释,现在运行一下程序,效果如下:

  好了,关于运行时权限的内容先介绍到这,下面进入主题——内容提供器。

7.2 内容提供器

  内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问到另一个程序中的数据,同时还能保证被访问数据的安全性。目前,使用内容提供器是 Android 实现跨程序共享数据的标准方式。

  不同于文件存储和 SharedPreferences 存储中的两种全局可读写操作模式,内容提供器可以选择只对哪一部分数据进行共享,从而保证程序中的隐私数据不会有泄露的风险。

7.2.1 访问其他程序中的数据

  内容提供器的用法有两种:

  • 使用现有的内容提供器来读取和操作相应程序中的数据
  • 创建自己的内容提供器给我们程序的数据提供外部访问接口

    7.2.1.1 ContentResolver 的基本用法

      每一个应用程序,借助 ContenResolver 类才能访问内容提供器中共享的数据,该类可通过 Context 中的 getContentResolver() 方法获取实例,该类提供的一系列对数据的增删查改方法不同于 SQLiteDataBase,是不接收表名参数的,而是用参数Uri(称为内容 URI) 代替。

  内容 URI 给内容提供器中的数据建立了唯一标识符,它由 authority(区分不同的应用程序)和 path(区分不同的表)组成。如某个程序包名为 com.example.app 且存在两张表 table1 和 table2 ,其标准格式写法如下:

  在得到了 内容 URI 字符串后,需要调用 Uri.parse() 方法把它解析成 Uri 对象才可作为参数传入,代码如下:

1
Uri uri = Uri.parse("content://com.example.app.provider/table1")
  • 查询操作
      现在就可以用这个 Uri 对象来查询 table1 表中的数据了,代码如下:
1
Curson curson = getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);

  下表对上面的参数进行了详细的解释:

  查询完后返回一个 Curson 对象,读取代码如下:

1
2
3
4
5
6
7
8
// 通过移动游标的位置来遍历Cursor的所有行
if (cursor != null){
while (cursor.moveToNext()){
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
  • 添加操作
      向 table1 表中添加一条数据:
1
2
3
4
5
6
// 将待添加的数据组装到 ContentValues 中
ContentValues values = new ContentValues();
values.put("column1","text");
values.put("column2",1);
// 调用 insert() 方法添加数据
getContentResolver().insert(uri, values);
  • 更新操作
      更新数据,把 column1 的值清空:
1
2
3
4
5
ContentValues values = new ContentValues();
// 清空
values.put("column1","");
// 调用 update() 方法更新数据
getContentResolver().update(uri, values, "column1 = ? and column2 = ?",new String[]{"text","1"});
  • 删除操作
      调用 ContentResolver 的 delete() 删除数据:
1
getContentResolver().delete(uri,  "column2 = ?", new String[]{"1"});

  以上就是 ContentResolver 中的增删查改方法。

7.2.1.2 读取系统联系人

  接下来举个例子来加深学习:利用内容提供器来读取系统联系人。

  由于模拟器上木有联系人,先向模拟器中创建几个联系人。联系人准备好后,首先在项目的 AndroidManifest.xml 中声明读取联系人的权限:

1
<uses-permission android:name="android.permission.READ_CONTACTS" />

  简单起见,在布局 activity_read_contact.xml 中放一个 ListView 来显示读取的联系人信息:

1
2
3
4
5
6
7
8
9
10
11
<RelativeLayout 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>

</RelativeLayout>

  接着修改 activity 中的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class ReadContactActivity extends AppCompatActivity {

ArrayAdapter<String> adapter;
List<String> contactsList = new ArrayList<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_read_contact);

// 获取 listView 的实例,设置适配器
ListView contacts_view = (ListView) findViewById(R.id.contacts_view);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,contactsList);
contacts_view.setAdapter(adapter);

// 判断是否授权
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1);
}else {
readContacts();
}

}

/**
* 读取联系人方法
*/
private void readContacts() {
Cursor cursor = null;
try{
// 查询联系人数据
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null, null);
if (cursor != null){
while (cursor.moveToNext()){
// 获取联系人姓名
String name = cursor.getString(cursor.getColumnIndex
(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
// 获取联系人号码
String number = cursor.getString(cursor.getColumnIndex
(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactsList.add(name + "\n" + number);
}
adapter.notifyDataSetChanged();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (cursor != null){
cursor.close();
}
}

}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
readContacts();
}else {
ToastUtils.showShort("你拒绝了权限请求");
}
break;

default:
break;
}
}
}

  运行程序,效果如下:

7.2.2 创建自己的内容提供器

  上一小节,介绍了如何访问其他应用程序得数据,其思路是获取应用程序的内容 URI 后借助 ContentResolver 进行 CRUD 操作就行了。接下来介绍创建自己的内容提供器。

7.2.2.1 创建步骤

  首先,新建一个类去继承 ContentProvider,重写它的6个抽象方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* 自己的内容提供器
* Created by KXwon on 2016/12/18.
*/

public class MyProvider extends ContentProvider{

// 初始化内容提供器的时候调用,返回true表示成功,false失败
@Override
public boolean onCreate() {
return false;
}

// 从内容提供器中查询数据
@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
return null;
}

// 向内容提供器中添加一条数据
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
return null;
}

// 从内容提供器中删除数据
@Override
public int delete(Uri uri, String s, String[] strings) {
return 0;
}

// 更新内容提供器中已有的数据
@Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}

// 根据传入的内容 URI 来返回 MIME 类型
@Override
public String getType(Uri uri) {
return null;
}
}

  之前提到一个标准的内容 URI 写法是这样的:

1
2
// 表示调用方期望访问的是 com.example.app 这个应用的 table1 表中的数据
content://com.example.app.provider/table1

  除此之外,还可以在内容 URI 后面加一个id,如下:

1
2
// 表示调用方期望访问的是 com.example.app 这个应用的 table1 表中 id 为 1 的数据
content://com.example.app.provider/table1/1

  内容 URI 的格式主要就只有以上两种,可以通过用通配符的方式来匹配这两种内容 URI,规则如下:
 (1) *:表示匹配任意长度的任意字符。
 (2)# :表示匹配任意长度的数字

  所以,一个能够匹配任意表的内容 URI 格式可写成:

1
content://com.example.app.provider/*

  一个能够匹配 table1 表中任意一行数据的内容 URI 格式可写成:

1
content://com.example.app.provider/table1/#

  然后,再借助 UriMatcher 这个类实现内容 URI 功能,修改 MyProvider 类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class MyProvider extends ContentProvider{

public static final int TABLE1_DIR = 0; //访问 table1 表中的所有数据
public static final int TABLE1_ITEM = 1;//访问 table1 表中的单条数据
public static final int TABLE2_DIR = 3; //访问 table2 表中的所有数据
public static final int TABLE2_ITEM = 4;//访问 table2 表中的单条数据

private static UriMatcher uriMatcher;

static {
// 创建 UriMatcher 实例
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 调用 addURI() 方法,此方法接收3个参数:authority、path、自定义代码
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);
}

// 从内容提供器中查询数据
@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
switch (uriMatcher.match(uri)){
case TABLE1_DIR:
// 查询 table1 表中的所有数据
break;
case TABLE1_ITEM:
// 查询 table1 表中的单条数据
break;
case TABLE2_DIR:
// 查询 table2 表中的所有数据
break;
case TABLE2_ITEM:
// 查询 table2 表中的单条数据
break;
default:
break;
}
. . .
}

. . .
}

  上述代码只是以 query() 方法做了个示范,其他3个增删改的方法也差不多。

  而 getType() 方法,是所有的内容提供器都必须提供的一个方法,用于获取 Uri 对象所对应的 MIME 类型。MIME 字符串主要由 3 部分组成,并有如下格式规定:

  • 必须由 vnd 开头;
  • 若内容 URI 以路径结尾,则后接 android.cursor.dir/,若内容 URI 以 id 结尾,则后接 android.cursor.item/;
  • 最后接上vnd..

  最后,实现 getType() 方法中的逻辑,完善 MyProvide r类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyProvider extends ContentProvider{

. . .

// 根据传入的内容 URI 来返回 MIME 类型
@Override
public String getType(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/vnd.com.example.app.provider.table2";
case TABLE2_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
default:
break;
}
return null;
}
}

  到这里,一个完整的内容提供器就创建完成了。

7.2.2.1 实现跨程序数据共享

  好了,例子来了。上一章项目中建立了 BookStore.db 数据库,里面有 book 表和 category 表这两张表,为简单起见,在上一章的基础上继续开发。

  首先,为项目创建个内容提供器,在 Android Studio 中右击 com.wonderful.myfirstcode.chapter7.provider包(你项目所在的包名)→New→Other→Content Provider,会弹出如下窗口:

  可以看到,这里把内容提供器命名为 DatabaseProvider,authority 指定为项目包名,Exported 表示是否允许外部程序访问我们的内容提供器,Enabled 表示是否启用这个内容提供器。两个勾选点击 Finish 完成创建。

  接着修改 DatebaseProvider 中的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
public class DataBaseProvider extends ContentProvider {

public static final int BOOK_DIR = 0; //访问 book 表中的所有数据
public static final int BOOK_ITEM = 1;//访问 book 表中的单条数据
public static final int CATEGORY_DIR = 3;
public static final int CATEGORY_ITEM = 4;

public static final String AUTHORITY = "com.wonderful.myfirstcode.chapter7.provider";

private static UriMatcher uriMatcher;

private MyDatabaseHelper dbHelper;

static {
// 创建 UriMatcher 实例
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 调用 addURI() 方法,此方法接收3个参数:authority、path、自定义代码
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() {
// 创建 MyDatabaseHelper 实例
dbHelper = new MyDatabaseHelper(getContext(),"BookStore.db",null,2);
// 返回true表示完成了创建或升级数据库
return true;
}

/**
* 查询数据
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)){
case BOOK_DIR:
// 查询 book 表中的所有数据
cursor = db.query("book",projection,selection,selectionArgs,
null,null,sortOrder);
break;

case BOOK_ITEM:
// 查询 book 表中的单条数据
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;
}

/**
* 添加数据
*/
@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;
}

/**
* 更新数据
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
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;
}

/**
* 删除数据
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
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 类型
*/
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)){
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.wonderful.myfirstcode." +
"chapter7.provider.book";

case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.wonderful.myfirstcode." +
"chapter7.provider.book";

case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.wonderful.myfirstcode." +
"chapter7.provider.category";

case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.wonderful.myfirstcode." +
"chapter7.provider.category";
}
return null;
}

}

  另外,内容提供器一定要在 AndroidManifest.xml 中注册才可使用。不过刚用 AS 创建的内容提供器已经帮我们自动注册完成了,如下:

1
2
3
4
5
6
 <provider
android:name=".chapter7.provider.DataBaseProvider"
android:authorities="com.wonderful.myfirstcode.chapter7.provider"
android:enabled="true"
android:exported="true">
</provider>

  现在,内容提供器已经创建好了,接下来新建一个项目 ProviderTest 来访问上面程序中的数据,记得清空上一章项目里的数据,以防造成干涉。

  先来编写下布局文件 activity_main.xml,添加4个按钮来增删查改,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="vertical">

<Button
android:id="@+id/btn_add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加数据"/>

<Button
android:id="@+id/btn_query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="查询数据"/>

<Button
android:id="@+id/btn_update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="更新数据"/>

<Button
android:id="@+id/btn_delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="删除数据"/>

</LinearLayout>

  然后修改 MainActivity 中的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class MainActivity extends AppCompatActivity {

private String newId;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 添加数据
Button btn_add_data = (Button) findViewById(R.id.btn_add_data);
btn_add_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("content://com.wonderful.myfirstcode.chapter7.provider/book");
ContentValues values = new ContentValues();
values.put("name", "第一行代码");
values.put("author", "郭霖");
values.put("pages", 1000);
values.put("price", 66.66);
Uri newUri = getContentResolver().insert(uri,values);
newId = newUri.getPathSegments().get(1);
}
});

// 查询数据
Button btn_query_data = (Button) findViewById(R.id.btn_query_data);
btn_query_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("content://com.wonderful.myfirstcode.chapter7.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 auhtor = cursor.getString(cursor.getColumnIndex("auhtor"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));

Log.d("MainActivity", "书名: " + name);
Log.d("MainActivity", "作者: " + auhtor);
Log.d("MainActivity", "页数: " + pages);
Log.d("MainActivity", "价格: " + price);
}
}
cursor.close();
}
});

// 更新数据
Button btn_update_data = (Button) findViewById(R.id.btn_update_data);
btn_update_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("content://com.wonderful.myfirstcode.chapter7.provider/book/"
+ newId);
ContentValues values = new ContentValues();
values.put("name", "第二行代码");
values.put("pages", 2000);
values.put("price", 88.88);
getContentResolver().update(uri,values,null,null);
}
});

// 删除数据
Button btn_delete_data = (Button) findViewById(R.id.btn_delete_data);
btn_delete_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("content://com.wonderful.myfirstcode.chapter7.provider/book/"
+ newId);
getContentResolver().delete(uri,null,null);
}
});
}
}

  代码很简单,不做解释了,现在运行一下项目,如下界面:

  点击 添加数据 按钮,此时数据就添加到上一章项目的数据库中了,点击 查询数据 按钮,打印日志如下:

  然后点击 更新数据 按钮,再次点击 查询数据 按钮查看打印日志如下:

  最后点击 删除数据 按钮,此时数据就没了,点击 查询数据 按钮也就查询不到数据了。

  以上,跨程序共享数据功能成功实现了。

  关于内容提供器相关内容就介绍到这。下篇文章将进入手机多媒体的学习。

Jason Xu wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!