在写一个群发短信APP时涉及到获取系统的联系人信息,添加联系人的事件为下:
mBtAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(intent,CODE_REQUEST);
}
});
重写onActivityResult:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CODE_REQUEST) {
if (resultCode == RESULT_OK) {
Uri contactUri = data.getData();
Cursor cursor = getContentResolver().query(contactUri, null, null, null, null);
cursor.moveToFirst();
String contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String number = getContactNumber(cursor);
if (!TextUtils.isEmpty(number)) {
mContactNums.add(number);
mContactNames.add(contactName);
addTag(contactName);
}
}
}
}
运行,选择Android 6.0的AVD,错误如下:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { dat=content://com.android.contacts/contacts/lookup/0r1-BFADB1C1/1 flg=0x1 }} to activity {com.example.festival_sms/com.example.festival_sms.SendMsgActivity}: java.lang.SecurityException: Permission Denial: reading com.android.providers.contacts.ContactsProvider2 uri content://com.android.contacts/data/phones from pid=2377, uid=10064 requires android.permission.READ_CONTACTS, or grantUriPermission()
at android.app.ActivityThread.deliverResults(ActivityThread.java:3699)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742)
at android.app.ActivityThread.-wrap16(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.SecurityException: Permission Denial: reading com.android.providers.contacts.ContactsProvider2 uri content://com.android.contacts/data/phones from pid=2377, uid=10064 requires android.permission.READ_CONTACTS, or grantUriPermission()
at android.os.Parcel.readException(Parcel.java:1599)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:421)
at android.content.ContentResolver.query(ContentResolver.java:491)
at android.content.ContentResolver.query(ContentResolver.java:434)
at com.example.festival_sms.SendMsgActivity.getContactNumber(SendMsgActivity.java:205)
at com.example.festival_sms.SendMsgActivity.onActivityResult(SendMsgActivity.java:180)
at android.app.Activity.dispatchActivityResult(Activity.java:6428)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3695)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742)
at android.app.ActivityThread.-wrap16(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
关键错误:
Caused by: java.lang.SecurityException: Permission Denial
检查AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
分析:
虽然在清单文件中已经定义了所需的权限,但是系统提示该权限为denied,想起之前看过某篇文章在Android 6.0 将权限分类处理。
为了更加一步确定我的错误原因在此,换用低于 6.0 的Android系统重新运行,运行成功,没再出现这个错误。
由此说明这确实是Android 6.0 权限管理的改动引发的问题,然后我查阅相关资料,以下为关键信息:
app的安装与获取权限的变化
对于6.0以下: 在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要默默忍受其一些不必要的权限。
而在6.0以后: 我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝,也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。
Android 6.0 权限分类
在Android 6.0中,权限被分类为正常权限和危险权限:
Normal Permissions
需要访问的数据一般不涉及用户的隐私,并且对其他应用程序风险很小。例如,允许打开手电筒是正常权限。如果一个应用程序声明它需要一个正常的权限,系统会自动授予应用程序的权限。
Dangerous Permission
需要数据或资源涉及到用户的私人信息,或可能会影响用户的存储数据或其他应用程序的操作。例如,读取用户的联系人是一个危险的权限。如果一个应用程序声明它需要一个危险的权限,用户必须显式地授予应用程序的权限。
关于read_contacts
因为read_contacts是一个危险的许可,我们需要请求用户在运行时授予它权限。那么如何请求用户给予我们的应用程序的明确权限呢?在我目前遇到这个问题之上改正的步骤如下:
对危险权限的处理步骤
添加一个新的实例变量permissions_request_read_contacts
这个变量用作申请用户授权权限时的匹配码,用于匹配onRequestPermissionsResult方法中的对权限的操作。
将对涉及到read_contacts权限的操作封装到一个方法中,然后对该方法添加if 条件,条件主要是为了判断当前系统是否为Android 6.0 以及 是否授予权限 ,修改后如下:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == CODE_REQUEST) { if (resultCode == RESULT_OK) { //如果当前版本大于等于Android 6.0,且该权限未被授予,则申请授权 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { //申请授权,第一个参数为要申请用户授权的权限;第二个参数为requestCode 必须大于等于0,主要用于回调的时候检测,匹配特定的onRequestPermissionsResult。 //可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。 requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, PERMISSIONS_REQUEST_READ_CONTACTS);
}else{
//如果该版本低于6.0,或者该权限已被授予,它则可以继续读取联系人。
mIntent = data;
getContacts(data);
}
}
}
}
3.重写onRequestPermissionsResult 方法 ,请求用户授予权限
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 用户成功授予权限
getContacts(mIntent);
} else {
Toast.makeText(this, "你拒绝了此应用对读取联系人权限的申请!", Toast.LENGTH_SHORT).show();
}
}
}