如果一个 APP 使用内容提供器对其数据提供了对外访问的接口,那么任何其他的 APP 都可以访问这部分的数据啦,像 Android 中的电话簿、短信等程序都提供了类似的访问接口。
ContentResolver 中提供了一系列方法用于对数据进行 CRUD 操作:
方法 | 说明 |
---|---|
insert() | 添加数据。 |
update() | 更新数据。 |
delete() | 删除数据。 |
query() | 查询数据。 |
这些方法使用 Uri 作为参数,它被称为内容 URI,它为数据建立了唯一标识符。内容 URI 由三部分组成:
* 协议声明 - content://
* authority - 使用程序包名来命名,用于区分不同的应用程序。
* path - 用于区分不同的表。
一个完整内容 URI 格式为:content://
得到了内容 URI 字符串后,需要调用 Uri.parse() 方法把它解析为 Uri 对象就可以作为参数传入 ContentResolver 方法,代码如下:
Uri uri = Uri.parse("content://com.deniro.app.provider/tableName");
Cursor cursor = getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
query 方法参数说明:
参数 | 说明 | 示例 |
---|---|---|
uri | 查询某个 APP 下的某一张表 | from table_name |
projection | 指定表列名 | select column1,column2 |
selection | 指定 where 约束条件 | where column = value |
selectionArgs | 为 selection 中的占位符提供具体的值 | - |
orderBy | 指定排序方式 | order by column1,column2 |
查询完成后会返回一个 Cursor 对象,读取逻辑是:通过移动游标来遍历 Cursor 中的所有行,然后再取出每一行中相应列的数据,代码如下:
if (cursor != null){
while (cursor.moveToNext()){
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
...
}
cursor.close();
}
insert 方法定义如下:
public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
@Nullable ContentValues values)
它还需要一个 ContentValues,作为需要新增的数据。
示例代码如下:
ContentValues values = new ContentValues();
values.put("column1","data");
values.put("column2",1);
getContentResolver().insert(uri, values);
update 方法定义如下:
public final int update(@RequiresPermission.Write @NonNull Uri uri,
@Nullable ContentValues values, @Nullable String where,
@Nullable String[] selectionArgs)
除了 uri 与 ContentValues ,update 方法还需要 where 条件约束的参数,来确定需要更新的数据范围。
示例代码如下:
ContentValues values = new ContentValues();
values.put("column1","new_data");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?",new String[]{"data","1"});
delete 方法定义如下:
public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where,
@Nullable String[] selectionArgs)
delete 方法所需要的参数与 update 方法相同。
示例代码如下:
getContentResolver().delete(uri, "column1 = ?", new String[]{"data"});
首先在模拟器中手工添加一些联系人信息,打开电话簿,点击 【ADD A CONTACT 】或者右下角的图标按钮:
第一次新增联系人时,会弹出【是否创建在线账号用于备份联系人信息】的选择框,这里我们选择 KEEP LOCAL:
然后输入联系人的姓名与手机号码:
接着,点击右上角的打勾按钮。
最后,以类似的方式再添加一个联系人:
现在,我们要开始使用内容提供器来读取系统联系人信息啦。
布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="net.deniro.android.contactstest.MainActivity">
<ListView
android:id="@+id/contacts"
android:layout_width="match_parent"
android:layout_height="match_parent">ListView>
LinearLayout>
我们把读取到的系统联系人信息放在一个 ListView 中展示。
Activity:
package net.deniro.android.contactstest;
import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
List contacts = new ArrayList<>();
ArrayAdapter adapter = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//设置 ListView 内容
ListView contactsView = (ListView) findViewById(R.id.contacts);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, contacts);
contactsView.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 mobile = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contacts.add(name + ":" + mobile);
}
//通知 ListView 数据已更新
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 {
Toast.makeText(this, "被拒绝", Toast.LENGTH_SHORT).show();
}
break;
default:
throw new RuntimeException("onRequestPermissionsResult");
}
}
}
这里使用了 ContentResolver 的 query() 方法来查询系统联系人的数据。ContactsContract.CommonDataKinds.Phone.CONTENT_URI
封装了内容 URI 字符串;接着,从 ContactsContract.CommonDataKinds.Phone
常量中,通过 Cursor 对象遍历获取联系人的姓名与手机号;然后通知 ListView 刷新列表。
因为读取系统联系人涉及到危险权限,所以这里先判断是否有权限。
最后记得在 AndroidManifest.xml 中声明权限:
<uses-permission android:name="android.permission.READ_CONTACTS"/>
为了示例的简洁,没有即时刷新联系人列表,所以如果有新增联系人,需要退出 APP 后再进入才会刷新联系人列表。
运行 APP:
是不是很简单呀O(∩_∩)O哈哈~