1.在Android6.0之前只需在AndroidManifest.xml文件写明权限即可。但是在Android6.0之后也就是SDK>=23的时候,一些隐私权限需要动态申请,而且在用户同意授权之后App才能拥有该权限。
如下9组权限需要动态申请。而且一组权限只要一个授权授权同意,全组都可用。
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
2.动态权限总共有三个方法:
ContextCompat.checkSelfPermission() 检查权限是否已经授权
ActivityCompat.checkSelfPermission() 检查权限是否已经授权
ActivityCompat.requestPermissions()动态申请权限,并弹出对话框。
onRequestPermissionsResult()在activity里面重写该方法该方法是权限申请之后的回调方法。
3.activity中的申请:
package com.ysl.mymaterialdesign.activity;
import android.Manifest.permission;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import java.util.Arrays;
public class PermissionActivity extends AppCompatActivity {
private static final int PERMISSION_CAMERA_CODE = 0;
private static final String PERMISSION_CAMERA = permission.CAMERA;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DealPermission(PERMISSION_CAMERA, PERMISSION_CAMERA_CODE);
}
private void DealPermission(String permission, int requestCode) {
int checkSelfPermission = ActivityCompat.checkSelfPermission(PermissionActivity.this, permission);
// int checkSelfPermission1 = ContextCompat.checkSelfPermission(PermissionActivity.this, permission.CAMERA);
if (checkSelfPermission == PackageManager.PERMISSION_GRANTED){
//权限已被授权
System.out.println("权限已被授权, 开始做事");
}else {
//权限未被授权,重新申请
System.out.println("权限未被授权,重新申请");
ActivityCompat.requestPermissions(PermissionActivity.this, new String[]{permission}, requestCode);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
System.out.println("requestCode = "+requestCode+" permissions = "+Arrays.toString(permissions)+" grantResults = "+Arrays.toString(grantResults));
if (requestCode == PERMISSION_CAMERA_CODE) {//申请时的返回码一一对应
//grantResults数组与权限字符串数组对应,里面存放权限申请结果
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
System.out.println("权限已被授权, 开始做事");
}else{
System.out.println("拒绝授权");
//如果是拒绝授权,可以弹出重要提示,或强制关闭页面。
}
}
}
}
代码中注释也写的很清楚,先检测权限有没有授权。若已经授权,然后再去做自己的业务。
4.上面的是申请了一个,假如申请一组:
写一个分组的类:
package com.ysl.mymaterialdesign.activity;
import android.Manifest;
import android.os.Build;
public class MyPermission {
public static final String[] CALENDAR;
public static final String[] CAMERA;
public static final String[] CONTACTS;
public static final String[] LOCATION;
public static final String[] MICROPHONE;
public static final String[] PHONE;
public static final String[] SENSORS;
public static final String[] SMS;
public static final String[] STORAGE;
static {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
CALENDAR = new String[]{};
CAMERA = new String[]{};
CONTACTS = new String[]{};
LOCATION = new String[]{};
MICROPHONE = new String[]{};
PHONE = new String[]{};
SENSORS = new String[]{};
SMS = new String[]{};
STORAGE = new String[]{};
} else {
CALENDAR = new String[]{
Manifest.permission.READ_CALENDAR,
Manifest.permission.WRITE_CALENDAR};
CAMERA = new String[]{
Manifest.permission.CAMERA};
CONTACTS = new String[]{
Manifest.permission.READ_CONTACTS,
Manifest.permission.WRITE_CONTACTS,
Manifest.permission.GET_ACCOUNTS};
LOCATION = new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION};
MICROPHONE = new String[]{
Manifest.permission.RECORD_AUDIO};
PHONE = new String[]{
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.CALL_PHONE,
Manifest.permission.READ_CALL_LOG,
Manifest.permission.WRITE_CALL_LOG,
Manifest.permission.USE_SIP,
Manifest.permission.PROCESS_OUTGOING_CALLS};
SENSORS = new String[]{
Manifest.permission.BODY_SENSORS};
SMS = new String[]{
Manifest.permission.SEND_SMS,
Manifest.permission.RECEIVE_SMS,
Manifest.permission.READ_SMS,
Manifest.permission.RECEIVE_WAP_PUSH,
Manifest.permission.RECEIVE_MMS};
STORAGE = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE};
}
}
}
package com.ysl.mymaterialdesign.activity;
import android.Manifest.permission;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import java.util.Arrays;
public class PermissionActivity extends AppCompatActivity {
private static final int PERMISSION_GROUP_CODE = 1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DealPermissionGroup(MyPermission.STORAGE, PERMISSION_GROUP_CODE);
}
private void DealPermissionGroup(String[] permissionGroup, int permissionGroupCode) {
int checkSelfPermission = ActivityCompat.checkSelfPermission(PermissionActivity.this, permissionGroup[0]);
if (checkSelfPermission == PackageManager.PERMISSION_GRANTED){
//权限已被授权
System.out.println("权限已被授权, 开始做事"+ permissionGroup[0]);
}else {
//权限未被授权,重新申请
System.out.println("权限未被授权,重新申请");
ActivityCompat.requestPermissions(PermissionActivity.this, permissionGroup, permissionGroupCode);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
System.out.println("requestCode = "+requestCode+" permissions = "+Arrays.toString(permissions)+" grantResults = "+Arrays.toString(grantResults));
if (requestCode == PERMISSION_GROUP_CODE){
for(int i=0; i<grantResults.length; i++){
if(grantResults[i]==PackageManager.PERMISSION_GRANTED){
System.out.println("已被授权");
} else {
System.out.println("拒绝授权");
//如果是拒绝授权,可以弹出重要提示,或强制关闭页面。
}
}
}
}
}
只需要申请每组中的任意一个,如果被授权了,其他的也会自动授权。
如果申请多组权限,调用多次DealPermissionGroup(String[] permissionGroup, int permissionGroupCode)即可。
5.注意点:
假如我现在动态申请READ_PHONE_STATE权限;
它属于的权限组:
PHONE = new String[]{
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.CALL_PHONE,
Manifest.permission.READ_CALL_LOG,
Manifest.permission.WRITE_CALL_LOG,
Manifest.permission.USE_SIP,
Manifest.permission.PROCESS_OUTGOING_CALLS};
调用:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DealPermissionGroup(MyPermission.PHONE, PERMISSION_LOCATION_CODE);
}
错误一:没有任何权限申请的提示框:因为清单文件中没有写。
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
错误二:PHONE下面有六个权限:
下面看申请的结果:
看日志,只有第一个授权了,其他的5个都没有授权。
下面我们把其他的5个也添加到清单文件中:
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
<uses-permission android:name="android.permission.USE_SIP"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
结果:
也就是说,在一组权限中,我们申请的时候,系统只会弹出来一个框来供用户授权。如果授权成功了。假如再申请这组的其他权限(在清单文件中申请),系统默认都会授权。但如果没有在清单文件中写这些权限。系统也是不会授权的。此时,假如我们直接使用这组中的其他权限,可能会出错。所以,在程序中,使用的权限,一定要在清单文件中写出来。
6.假如我一次申请了4组权限:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DealPermission(PERMISSION_CAMERA, PERMISSION_CAMERA_CODE);
DealPermissionGroup(MyPermission.STORAGE, PERMISSION_STORAGE_CODE);
DealPermissionGroup(MyPermission.LOCATION, PERMISSION_LOCATION_CODE);
DealPermissionGroup(MyPermission.PHONE, PERMISSION_LOCATION_CODE);
}
测试发现:Android6.0上,程序以启动会弹出四个授权框,让我点击四次来授权。但是7.0,8.0,9.0都只会弹出第一个授权相机的框,其他的都没有弹出来。只有退出app,再次进来,才会弹出第二个提示框。也就是说,我要退出四次,才能把需要的权限申请完成。这显然是不合理的,假如第一次就要使用后面的权限,会导致app报错的。
7.基于上面的错误,我们可以使用第三方库
PermissionDispatcher : Androidstudio还有插件,直接点击两下就完成了。
我的:
//依赖
implementation('com.github.hotchemi:permissionsdispatcher:3.1.0')
annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:3.1.0'
//代码
package com.ysl.mymaterialdesign.activity;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import permissions.dispatcher.NeedsPermission;
import permissions.dispatcher.OnNeverAskAgain;
import permissions.dispatcher.OnPermissionDenied;
import permissions.dispatcher.OnShowRationale;
import permissions.dispatcher.PermissionRequest;
import permissions.dispatcher.RuntimePermissions;
@RuntimePermissions
public class PermissionDispatcherActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PermissionDispatcherActivityPermissionsDispatcher.needsWithPermissionCheck(this);
}
@NeedsPermission({Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
void needs() {
Toast.makeText(this, "getNeeds", Toast.LENGTH_SHORT).show();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionDispatcherActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
}
@OnShowRationale({Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
void show(final PermissionRequest request) {
new AlertDialog.Builder(this)
.setMessage("此APP需要以下权限,下一步将请求权限")
.setPositiveButton("下一步", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
request.proceed();//继续执行请求
}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
request.cancel();//取消执行请求
}
})
.show();
}
@OnPermissionDenied({Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
void denied() {
Toast.makeText(this, "已拒绝一个或以上权限", Toast.LENGTH_SHORT).show();
}
@OnNeverAskAgain({Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
void neverAskAgain() {
Toast.makeText(this, "已拒绝一个或以上权限,并不再询问", Toast.LENGTH_SHORT).show();
}
}
优点:有插件,集成简单;申请多个时,非常方便
缺点:没有权限检测的方法,当同时申请多个权限时,当有多个拒绝的,或多个拒绝并不再询问的,只会提示出来,而不能指出到底哪个被拒绝了。
详细使用请看:
https://blog.csdn.net/s13383754499/article/details/79034758
EasyPermissions: Google官方封装的库
我的:
//依赖
implementation 'pub.devrel:easypermissions:2.0.0'
//代码
package com.ysl.mymaterialdesign.activity;
import android.Manifest;
import android.Manifest.permission;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;
import java.util.List;
import pub.devrel.easypermissions.AppSettingsDialog;
import pub.devrel.easypermissions.EasyPermissions;
public class EasyPermissionsActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks, EasyPermissions.RationaleCallbacks{
private static final int CAMERA_REQUESTCODE = 101;
private static final int LOCATION_CONTACTS_REQUESTCODE = 102;
private static final String TAG = "EasyPermissionsActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestCamera();
requestLocationAndContacts();
easyCheckPermissions(this, new String[]{permission.CAMERA});
}
public void requestCamera() {
if (EasyPermissions.hasPermissions(this, Manifest.permission.CAMERA)) {
Toast.makeText(this, "已授权 Camera", Toast.LENGTH_LONG).show();
} else {
// request for one permission
EasyPermissions.requestPermissions(this, "需要相机权限",
CAMERA_REQUESTCODE, Manifest.permission.CAMERA);
}
}
public void requestLocationAndContacts() {
String[] perms = { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_CONTACTS };
if (EasyPermissions.hasPermissions(this, perms)) {
Toast.makeText(this, "已授权 联系人和 位置信息", Toast.LENGTH_LONG).show();
} else {
// request for both permissions
EasyPermissions.requestPermissions(this, "需要位置和联系人权限",
LOCATION_CONTACTS_REQUESTCODE, perms);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
Log.d(TAG, "onPermissionsGranted ---> requestCode = "+requestCode+" perms = "+perms);
switch (requestCode){
case CAMERA_REQUESTCODE:
Toast.makeText(this, "已获取权限"+perms, Toast.LENGTH_SHORT).show();
break;
case LOCATION_CONTACTS_REQUESTCODE:
Toast.makeText(this, "已获取权限"+perms, Toast.LENGTH_SHORT).show();
Log.d(TAG, "已获取权限"+perms);
break;
}
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
Log.d(TAG, "onPermissionsDenied ---> requestCode = "+requestCode+" perms = "+perms);
switch (requestCode) {
case CAMERA_REQUESTCODE:
Toast.makeText(this, "已拒绝权限" + perms, Toast.LENGTH_SHORT).show();
break;
case LOCATION_CONTACTS_REQUESTCODE:
Toast.makeText(this, "已拒绝权限"+ perms, Toast.LENGTH_SHORT).show();
Log.d(TAG, "已拒绝权限"+ perms);
break;
}
//假如设置了不再询问,就弹出去设置页面修改的选项
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
Toast.makeText(this, "已拒绝权限" + perms + "并不再询问" , Toast.LENGTH_SHORT).show();
Log.d(TAG, "已拒绝权限" + perms + "并不再询问");
new AppSettingsDialog
.Builder(this)
.setRationale("此功能需要" + perms + "权限,否则无法正常使用,是否打开设置")
.setPositiveButton("好")
.setNegativeButton("不行")
.build()
.show();
}
}
@Override
public void onRationaleAccepted(int requestCode) {
}
@Override
public void onRationaleDenied(int requestCode) {
}
//使用EasyPermissions查看权限是否已申请
//可以传一个或多个权限字符串,也可以把这些放在字符串数组中来传参
//传入多个的时候,只要有一个没有授权,就会返回false;全部都授权了,才会返回true。
public boolean easyCheckPermissions(Context context, String ... permissions) {
boolean hasPermissions = EasyPermissions.hasPermissions(context, permissions);
return hasPermissions;
}
}
优点:可以申请一个或多个权限;可以检测一个或多个权限;申请多个权限时,多个权限的申请结果很清晰,谁同意了,谁被拒绝了,谁被拒绝并且不再询问了。而且当不再询问时,还有提示框可以跳转到设置页面(对于私下把app权限禁止了,在app中使用此功能时,做提示很有用)。
缺点:写起来代码稍多。
详细使用请看:
https://blog.csdn.net/zhu522959034/article/details/73647860
RxPermissions:可以配合rxJava或rxJava2一起使用
我的:
//依赖
implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
// implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.3@aar'
implementation 'com.github.tbruyelle:rxpermissions:0.10.2'
//代码
package com.ysl.mymaterialdesign.activity;
import android.Manifest;
import android.Manifest.permission;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.tbruyelle.rxpermissions2.Permission;
import com.tbruyelle.rxpermissions2.RxPermissions;
import io.reactivex.functions.Consumer;
public class RxPermissionActivity extends AppCompatActivity {
private static final String TAG = "RxPermissionActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestPermissions();
checkPermissionsIsGranted(this);
}
private void requestPermissions() {
RxPermissions rxPermission = new RxPermissions(RxPermissionActivity.this);
rxPermission
.requestEach(Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA)
.subscribe(new Consumer<Permission>() {
@Override
public void accept(Permission permission) throws Exception {
if (permission.granted) {
// 用户已经同意该权限
Log.d(TAG, permission.name + " is granted.");
} else if (permission.shouldShowRequestPermissionRationale) {
// 用户拒绝了该权限,没有选中『不再询问』(Never ask again),那么下次再次启动时。还会提示请求权限的对话框
Log.d(TAG, permission.name + " is denied. More info should be provided.");
} else {
// 用户拒绝了该权限,而且选中『不再询问』
Log.d(TAG, permission.name + " is denied.并且选中了不再询问。");
}
}
});
}
//检查某个权限是否被申请,只支持单个权限的检测
public boolean checkPermissionsIsGranted(FragmentActivity activity) {
RxPermissions permissions = new RxPermissions(activity);
permissions.setLogging(true);
boolean granted = permissions.isGranted(permission.WRITE_EXTERNAL_STORAGE);
Log.d(TAG, "checkPermissionsIsGranted ---> " + granted);
return granted;
}
}
优点:代码简洁,少;可以和rxJava配合使用;有单个权限检测功能;申请多个权限时,多个权限的申请结果很清晰,谁同意了,谁被拒绝了,谁被拒绝并且不再询问了。
缺点:依赖中要引入rxJava,rxAndroid包,rxJava版本不同对应的rxPermission版本也不一致。
详细使用请看:
https://www.jianshu.com/p/c1219d1d2401
以上只是个人感觉,三者详细比较可以看:
目前最流行的运行时权限请求框架PermissionsDispatcher、RxPermissions和easypermissions的使用和对比