在Android M添加的权限机制中,Google将权限分为两类:
- Normal Permissions(普通权限):不涉及用户隐私,不需要用户进行授权,比如访问网络等
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_NOTIFICATION_POLICY
android.permission.ACCESS_WIFI_STATE
android.permission.ACCESS_WIMAX_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE_NETWORK_STATE
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.CHANGE_WIMAX_STATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND_STATUS_BAR
android.permission.FLASHLIGHT
android.permission.GET_ACCOUNTS
android.permission.GET_PACKAGE_SIZE
android.permission.INTERNET
android.permission.KILL_BACKGROUND_PROCESSES
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST_INSTALL_PACKAGES
android.permission.SET_TIME_ZONE
android.permission.SET_WALLPAPER
android.permission.SET_WALLPAPER_HINTS
android.permission.SUBSCRIBED_FEEDS_READ
android.permission.TRANSMIT_IR
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SYNC_SETTINGS
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
com.android.launcher.permission.UNINSTALL_SHORTCUT
- Dangerous Permission(危险权限):涉及到用户隐私,需要用户进行授权,比如相机访问、读取SD卡等
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
运行时权限只适合APP运行在Android 6.x以上的机器,对于危险权限Google官方也对其进行了分组,只要一组中的某一权限被授权,同组的权限也同样会被授权。
操作步骤
新的权限机制操作步骤如下
在Androidmanifest.xml文件声明相关权限
通过ContextCompat.checkSelfPermission方法检查某项权限被授予情况
通过ActivityCompat.shouldShowRequestPermissionRationale方法检查权限是否被永久拒绝需要向用户说明
通过Activity.requestPermissions申请授权
处理Activity.onRequestPermissionsResult权限回调
Google给出的Demo实现方式
/**
* Check that all given permissions have been granted by verifying that each entry in the
* given array is of the value {@link PackageManager#PERMISSION_GRANTED}.
*
* @see Activity#onRequestPermissionsResult(int, String[], int[])
*/
public static boolean verifyPermissions(int[] grantResults) {
// At least one result must be checked.
if(grantResults.length < 1){
return false;
}
// Verify that each required permission has been granted, otherwise return false.
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
public void showContacts(View v) {
Log.i(TAG, "Show contacts button pressed. Checking permissions.");
// 验证所有联系人权限是否已经获得
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// 通讯录权限未获得
Log.i(TAG, "Contact permissions has NOT been granted. Requesting permissions.");
requestContactsPermissions();
} else {
// 通讯录权限已获得,显示通讯录Fragment
Log.i(TAG,
"Contact permissions have already been granted. Displaying contact details.");
showContactDetails();
}
}
/**
* 请求通讯录权限
* 如果权限已经被拒绝了,那么会弹出一个弹框提醒用户去授予权限,否则会直接请求
*/
private void requestContactsPermissions() {
// BEGIN_INCLUDE(contacts_permission_request)
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_CONTACTS)
|| ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_CONTACTS)) {
// 如果没有授予许可,请向用户提供额外的理由来让用户理解请求此权限的原因
// 例如,如果以前已经拒绝了权限请求
Log.i(TAG,"Displaying contacts permission rationale to provide additional context.");
// 显示一个带有说明和按钮的SnackBar来触发请求。
Snackbar.make(mLayout, R.string.permission_contacts_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_CONTACT,
REQUEST_CONTACTS);
}
})
.show();
} else {
// 通讯录权限尚未授予,直接请求
ActivityCompat.requestPermissions(this, PERMISSIONS_CONTACT,
REQUEST_CONTACTS);
}
// END_INCLUDE(contacts_permission_request)
}
//权限处理结果的回调
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,@NonNull int[] grantResults) {
if (requestCode == REQUEST_CONTACTS) {
Log.i(TAG, "Received response for contact permissions request.");
// 对请求的所有联系人权限再核实一遍
if (PermissionUtil.verifyPermissions(grantResults)) {
// 所有权限都已经获得,显示联系人页面
Snackbar.make(mLayout, R.string.permision_available_contacts,
Snackbar.LENGTH_SHORT)
.show();
} else {
Log.i(TAG, "Contacts permissions were NOT granted.");
Snackbar.make(mLayout, R.string.permissions_not_granted,
Snackbar.LENGTH_SHORT)
.show();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions,grantResults);
}
}
关键的几个方法
1.检查是否拥有指定权限
android.support.v4.content.ContextCompat.java
/**
* Determine whether <em>you</em> have been granted a particular permission.
*
* @param permission The name of the permission being checked.
*
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
* permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
*
* @see android.content.pm.PackageManager#checkPermission(String, String)
*/
public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
}
2.判断是否彻底拒绝而了某项权限而需要对用户进行说明
android.support.v4.app.ActivityCompat.java
/**
* Gets whether you should show UI with rationale for requesting a permission.
* You should do this only if you do not have the permission and the context in
* which the permission is requested does not clearly communicate to the user
* what would be the benefit from granting this permission.
* <p>
* For example, if you write a camera app, requesting the camera permission
* would be expected by the user and no rationale for why it is requested is
* needed. If however, the app needs location for tagging photos then a non-tech
* savvy user may wonder how location is related to taking photos. In this case
* you may choose to show UI with rationale of requesting this permission.
* </p>
*
* @param activity The target activity.
* @param permission A permission your app wants to request.
* @return Whether you can show permission rationale UI.
*
* @see #checkSelfPermission(android.content.Context, String)
* @see #requestPermissions(android.app.Activity, String[], int)
*/
public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
@NonNull String permission) {
if (Build.VERSION.SDK_INT >= 23) {
return ActivityCompatApi23.shouldShowRequestPermissionRationale(activity, permission);
}
return false;
}
3.发出权限请求
android.app.Activity
/**
* Requests permissions to be granted to this application. These permissions
* must be requested in your manifest, they should not be granted to your app,
* and they should have protection level {@link android.content.pm.PermissionInfo
* #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
* the platform or a third-party app.
* <p>
* Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
* are granted at install time if requested in the manifest. Signature permissions
* {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
* install time if requested in the manifest and the signature of your app matches
* the signature of the app declaring the permissions.
* </p>
* <p>
* If your app does not have the requested permissions the user will be presented
* with UI for accepting them. After the user has accepted or rejected the
* requested permissions you will receive a callback on {@link
* #onRequestPermissionsResult(int, String[], int[])} reporting whether the
* permissions were granted or not.
* </p>
* <p>
* Note that requesting a permission does not guarantee it will be granted and
* your app should be able to run without having this permission.
* </p>
* <p>
* This method may start an activity allowing the user to choose which permissions
* to grant and which to reject. Hence, you should be prepared that your activity
* may be paused and resumed. Further, granting some permissions may require
* a restart of you application. In such a case, the system will recreate the
* activity stack before delivering the result to {@link
* #onRequestPermissionsResult(int, String[], int[])}.
* </p>
* <p>
* When checking whether you have a permission you should use {@link
* #checkSelfPermission(String)}.
* </p>
* <p>
* Calling this API for permissions already granted to your app would show UI
* to the user to decide whether the app can still hold these permissions. This
* can be useful if the way your app uses data guarded by the permissions
* changes significantly.
* </p>
* <p>
* You cannot request a permission if your activity sets {@link
* android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
* <code>true</code> because in this case the activity would not receive
* result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}.
* </p>
* <p>
* The <a href="http://developer.android.com/samples/RuntimePermissions/index.html">
* RuntimePermissions</a> sample app demonstrates how to use this method to
* request permissions at run time.
* </p>
*
* @param permissions The requested permissions. Must me non-null and not empty.
* @param requestCode Application specific request code to match with a result
* reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
* Should be >= 0.
*
* @see #onRequestPermissionsResult(int, String[], int[])
* @see #checkSelfPermission(String)
* @see #shouldShowRequestPermissionRationale(String)
*/
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
if (mHasCurrentPermissionsRequest) {
Log.w(TAG, "Can reqeust only one set of permissions at a time");
// Dispatch the callback with empty arrays which means a cancellation.
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
}
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
4.在Activity中接受用户处理结果回调
android.app.Activity
/**
* Callback for the result from requesting permissions. This method
* is invoked for every call on {@link #requestPermissions(String[], int)}.
* <p>
* <strong>Note:</strong> It is possible that the permissions request interaction
* with the user is interrupted. In this case you will receive empty permissions
* and results arrays which should be treated as a cancellation.
* </p>
*
* @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
* @param permissions The requested permissions. Never null.
* @param grantResults The grant results for the corresponding permissions
* which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
* or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
*
* @see #requestPermissions(String[], int)
*/
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
/* callback - no nothing */
}
为了便于使用可以在此基础上进行封装,可以参考下面这篇文章中的封装方式
http://www.jianshu.com/p/4a60b064a0ab