一、概述:
内容提供程序有助于应用管理其自身和其他应用所存储数据的访问,并提供与其他应用共享数据的方法。它们会封装数据,并提供用于定义数据安全性的机制。内容提供程序是一种标准接口,可将一个进程中的数据与另一个进程中运行的代码进行连。实现内容提供程序大有好处。最重要的是,通过配置内容提供程序,您可以使其他应用安全地访问和修改您的应用数据。
二、访问提供程序
对于每一个应用程序来说,如果想要访问内容提供器中共享的数据,就一定要借助ContentResolver类,可以通过Context中的getContentResolver()方法获取到该类的实例。(ContentResolver 对象会与提供程序对象(即实现 ContentProvider 的类实例)进行通信。提供程序对象从客户端接收数据请求、执行请求的操作并返回结果。)ContentResolver中提供了一系列的方法用于对数据进行CRUD操作,其中insert()方法用于添加数据,update()方法用于更新,delete()方法用于删除数据,query()方法用于查询数据。和SQLite中进行CRUD操作的方法类似,只是在参数上面有些不同。
从界面访问ContentProvider的常用模式会通过使用CursorLoader,在后台运行异步查询。界面中的Activity或Fragment会调用查询的CursorLoader,其转而使用ContentResolver来获取ContentProvider。
ContentResolver中的增删改查方法都是不接收表名参数的,而是使用一个Uri参数代替,这个参数被称为内容URI。内容URI给内容提供器中的数据建立唯一标识符,他一般由两个部分组成:authority和path。authority是用于对不同的应用程序做区分的,一般为了冲突,都会采用程序包名的方式来命名。比如某个程序的的包名是com.example.app,那么这个程序对应的authority就是com.example.app.provider。path则是用于对同一应用程序中的不同的表做区分的,通常会添加到authority后面。比如此时数据库中有两个表table1和table2,这是path就可以命名为/table1和/table2。然后将authority和path进行组合,再在字符串头部加上协议声明,内容URI标准写法如下:
content://com.example.app.provider/table1
content://com.example.app.provider/table2
在得到内容URI字符串后,还需要将它解析成Uri对象才可以作为参数传入,解析代码如下:
Uri uri=Uri.parse("content://com.example.app.provider/table1");
//调用Uri.parse()方法,就可以把URI内容字符串解析成Uri对象了
现在就可以用这个Uri对象来查询table1中的数据了。
// Queries the user dictionary and returns results
cursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // The content URI of the words table
projection, // The columns to return for each row
selectionClause, // Selection criteria
selectionArgs, // Selection criteria
sortOrder); // The sort order for the returned rows
查询完成后返回的仍是一个Cursor对象,这时我们就可以将数据从Cursor对象中逐一读取出来了 。读取的思路是通过移动游标的位置来遍历Cursor中的所有行,然后再取出每一行中相应列的数据,代码如下:
if(cursor!=null)
{
while(cursor.moveToNext())
{
String column1=cursor.getString(cursor.getColumnIndex("colunm1"));
int column2=cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
添加数据,将数据组装到ContentValues中,再调用ContentResolver的insert()方法。将Uri和ContentValues作为参数传入即可:
ContentValues values=new ContentValues();
values.put("column1","text");
values.put("column2",999);
getContentResolver().insert(uri,values);
更新数据:
ContentValues values=new ContentValues();
values.put("column1","update");
getContentResolver().update(uri,values,"column1=? and column2=?",new String[]{"text","999"});
//将column1=text,column2=999的数据中的column1内容修改为“update”
删除数据
getContentResolver().delete(uri,"column2=?",new String[]{"999"});
三、读取系统联系人-实践
ListView布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listview_contacts"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
获取权限+读取系统联系人
public class ContactsTestActivity extends AppCompatActivity {
private ListView listViewContacts;
ArrayAdapter<String> adapter;
List<String> contactList=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contacts_test);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1);
}
else{
readContacts();
}
listViewContacts=(ListView)findViewById(R.id.listview_contacts);
adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,contactList);
listViewContacts.setAdapter(adapter);
}
//读取系统联系人
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 displayName=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
//获取联系人手机号
String number=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactList.add(displayName+"\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) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch(requestCode)
{
case 1:
if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED)
{
readContacts();
}
else
{
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) {
Toast.makeText(this,"我们需要获得READ_CONTACTS权限才可以访问通讯录!!!",Toast.LENGTH_SHORT).show();
// *异步*向用户显示说明-不要阻止
//此线程等待用户的响应!之后用户
//看到说明,然后重试以请求许可。
} else {
// 无需解释;请求许可
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS},1);
}
}
break;
default:
}
}
}
最后,还需要在AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.READ_CONTACTS"/>