内容提供器
权限
以拨号(之前是打开拨号界面, 此处是指直接拨号)为例分析权限申请和使用的过程.
还是基于Intent, 和之前一样, 给一个按钮增加点击的监听器, 重载onClick, 写入如下内容. 为防止权限被拒绝时程序崩溃, 所以此处需要捕获异常.
1
2
3
4
5
6
7
8
try {
// DIAL 是 打开拨号界面, CALL 是直接拨号,必须申请权限
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
} catch (SecurityException e) {
e.printStackTrace();
}
然后修改 AndroidManifest 文件, 加入如下内容
1
<uses-permission android:name="android.permission.CALL_PHONE" />
但是以上功能仅能在 安卓6.0以下才能成功. 更高的版本还需要运行时权限申请
完整代码
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
package com.example.dvtester;
// Import已被删掉
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
openWebView();
testCall();
}
void testCall() {
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();
}
}
});
}
void call() {
// 已经有权限了,可以用intent来拨号了
try {
// DIAL 是 打开拨号界面, CALL 是直接拨号,必须申请权限
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
} catch (SecurityException e) {
e.printStackTrace();
}
}
// 无论成功与否,发出申请(requestPermission)后这个函数都会回调
// 第一个参数是请求码,与申请时的相同,第二个是当时请求的权限数组 , 第三个是申请的结果数组,也就是用于和PackageManager.PERMISSION_GRANTED比较
@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(this, "You deied the permission", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
}
通过上面的代码, 点击按钮后, 会弹出权限申请的窗口, 如果拒绝, 那么下次再点就仍会弹出询问窗口. 如果接受, 则永久接受, 除非到设置里进行拒绝.
内容提供器
URI
URI , 给内容提供器的数据建立了唯一的标识符. 分为两部分, authority和path, 标准写法就是 content://authority+path
例如 content://com.example.app.provider/table1
1
Uri uri = Uri.parse("content://com.example.app.provider/table1");
查询
getContentResolver() 就是访问内容提供器的方法,
而查询方法如下, 使用 getContentResolver().query()方法
1
Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
此方法接受五个参数, 第一个是 URI , 第二个是查询的列名, 第三个是约束条件, 第四个是约束条件中的占位符的具体值, 第五个是查询结果的排序方式.
最后用完cursor记得关闭( cursor.close()
)
ArrayAdapter
ArrayAdapter是数组适配器, 这个地方目前还是不太清楚. 搜到的资料说是为了MVC结构而引入的, 让数据和视图分离. 适配器负责存取数据, 然后和View绑定, 数据变化不会影响视图.
检索联系人
这个和上面的拨号权限思路相仿. 先判断有没有权限, 有就直接读取, 没有就尝试申请.
读取界面
首先先在模拟器中新建两个联系人. 在xml中新增一个 ListView
1
2
3
4
<ListView
android:id="@+id/contacts_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
读取联系人
在活动中增加如下代码, 这就是获取权限之后要执行的代码段.
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
// 成员变量,分别是适配器和联系人数组,详情去看MVC架构
// 此处的适配器充当控制器的一部分,用以连接视图和数据
// 将constactsList的数据和视图上的ListView同步更改
ArrayAdapter<String> adapter;
List<String> contactsList = new ArrayList<>();
private void readContects() {
Cursor cursor = null;
try {
// 使用内容提供器的查询功能
// 此处的URI不用自己解析了,安卓已经内置好了,直接调用就可.
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));
// 这个是类中的成员变量
contactsList.add(displayName + "\n" + number);
}
adapter.notifyDataSetChanged();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
申请权限
然后申请权限, 这个和之前基本一样, 此处将返回码设置为2, 以作区分.
1
2
3
4
5
6
7
8
9
10
11
void testReadContacts() {
ListView contactsView = (ListView) findViewById(R.id.contacts_view);
// 下行这个 simple_list_item_1 是自带的
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contactsList);
contactsView.setAdapter(adapter);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 2);
} else {
readContects();
}
}
然后在之前的申请权限的回调函数里面继续添加此次回调的逻辑, 如果申请成功, 就读取联系人.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@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(this, "You deied the permission", Toast.LENGTH_SHORT).show();
}
break;
case 2:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 刚才的权限申请成功,可以读取了
readContects();
} else {
Toast.makeText(this, "You deied the permission", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}