Android学习(8)

跨程序共享数据——内容提供器

跨程序共享数据的意义:针对一些可以让其他程序进行二次开发的基础性数据,例如系统的电话簿程序,和短信、媒体库等程序,实现跨程序数据共享的功能,使用的技术就是内容提供器。
PS:针对账号、密码等隐私数据显然不能贡共享给其他程序。

内容提供器简介

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

运行时权限

运行时权限即用户不需要在安装软件时一次性授权所有申请的权限,而是可以在软件使用过程中再对某一项权限申请进行授权。

在程序运行时申请权限

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
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button makeCall = (Button)findViewById(R.id.make_call);
makeCall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.CALL_PHONE)!=PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CALL_PHONE},1);
}else{
call();
}

}
});
}

private void call(){
try {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
} catch (SecurityException e) {
e.printStackTrace();
}
}

@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 {
Toast.makeText(MainActivity.this,"You denied the permission",Toast.LENGTH_SHORT).show();
}
}
}

访问其他程序中的数据

ContetnResolver的用法

对每一个应用程序而言,要想访问内容提供器共享的数据,就要借助ContentResolver。

不同于SQLiteDatabase, 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就变成了com.example.app.provider/table1和com.example.app.provider/table2。

以下程序为实现读取通讯录联系人:

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
public class MainActivity extends AppCompatActivity {
List<String> contactList = new ArrayList<>();
ArrayAdapter<String> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView contact_view = (ListView)findViewById(R.id.contacts_list);
adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,contactList);
contact_view.setAdapter(adapter);
if(ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS)!=PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.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 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) {
switch(requestCode){
case 1:
if(grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED){
readContacts();
}else{
Toast.makeText(this,"You denied the permission",Toast.LENGTH_SHORT).show();
}
}
}
}

注意:需要在AndroidManifest.xml文件中,声明读取通讯录的权限

创建自己的内容提供器

可以通过新建一个类去继承ContentProvider的方式来创建一个自己的内容提供器。ContentProvider类中由6个抽象方法,在使用子类继承它时,需要将这6个方法全部重写。

一个标准的内容URI写法为:
content://com.example.app.provider/table1
或者
content://com.example.app.provider/table1/1
这表示访问的是table表中id为1的数据

通配符匹配内容URI的规则如下:
*:表示匹配任意长度的任意字符。
#:表示匹配任意长度的数字。

因此,一个能匹配任意表的内容URI可以写成:
content://com.example.app.provider/*

匹配table表中任意一行数据的内容URI格式可以写成:
content://com.example.app.provider/table/#