运行时权限

###什么时候用

这是在Android 6.0中加入的功能,就是说用户可以在软件使用过程中对于某一项权限进行申请。但是,并不是所有的权限都需要申请,权限可以分为两类,一类是普通权限,一类是危险权限。总之,在Api>23的手机软件,在运行危险权限时候,需要进行动态的申请,也就是运行时申请。如图示危险权限:

这里写图片描述

###动态申请时候所必须知道的

方法一:
ContextCompat.checkSelfPermission();
参数一:context 对象
参数二:String perimisson 权限名,比如:Manifest.permission.WRITE_EXTERNAL_STORAGE。
作用:检查程序有没有某个权限。
返回值:PERMISSION_GRANTED(其实是0),表示有某个权限,PERMISSION_DENIED(其实是-1)表示没有某个权限。
方法二:
作用:完成请求某个权限的功能
ActivityCompat.requeestPermissions();
参数一:activity
参数二:String[] permissions 权限数组,表明可以一次申请多个权限
参数三:requestCode 请求码,全局唯一的并且>=0
说明:使用这个方法时候,还有一个回调方法onRequestPermissionsResult
但是用上面的请求时候,这个回调方法只能在Activity中有效果,哪怕你是在Fragment中发起的请求权限,那么在Fragment中,动态申请权限时候,一般采用
MineStudentFragment.this.requestPermissions( new String[]{permission}, permissionCode);
这样的话,回调也会在相应的Fragment中实现了
方法三:
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {}
说明:这是一个回调方法,在发起动态请求的时候,最后结果就会到这个方法
参数一:发起权限请求时候的请求码
参数二:permissions 是你发起请求的时候填写的权限数组
参数三:表示申请权限后,每个权限的结果,具体如图:

假设自己发起如下的权限申请,下面,打印结果:

ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CAMERA,Manifest.permission.CALL_PHONE
                    ,Manifest.permission.USE_FINGERPRINT,Manifest.permission.USE_SIP,Manifest.permission.SEND_SMS,Manifest.permission.WRITE_EXTERNAL_STORAGE
                    ,Manifest.permission.WRITE_CALENDAR,Manifest.permission.ADD_VOICEMAIL
            },1);

这里写图片描述

可以发现,是一一对应的,而且,申请成功的,对应的grantResults为0 ,反之,为-1

###必须注意的几点

1、你想动态申请某个权限时候,必须在清单文件中也加上,一方面,这样Android6.0
以下的也具有了这个权限,另一方面,你不在清单文件中加上,在Android 6.0版本往
上,根本不会弹窗询问用户,会默认的返回-1,也就是申请权限没有通过。
2、假设有三个权限,用户在选择的时候,第三个没有允许,那么在一次申请权限的话,
第三个还会再问一遍,除非用户勾选了禁止后不在询问的选项。而用户允许的权限,也
不会在弹窗询问了。用户在设置中,手动给权限了,那么在运行代码的时候,也是不在
询问。默认的就返回为-1了
3、假设不加版本判断,在低版本上也运行动态申请代码的部分,运行到这里的时候,也
是会执行,但是不会去弹窗询问用户,并且结果也是会反馈到
onRequestPermissionsResult中,但是,只要是在清单文件中注册了,就会设置
成0
4、
如果你嫌弃麻烦的话,不如将都动态申请的权限都添加进去,它在动态申请的时候,都会检测,有的不会再
弹出来申请,没有的就会弹窗申请。
5
建议在权限申请回调里面,添加grantResults.length ==0 ,然后 return ,多加一层判断 ,可以防止空指针问题

##第三方库PermissionsDispatcher
翻译过来:权限调度,
针对于android 6.0权限建立的一个库
https://github.com/permissions-dispatcher/PermissionsDispatcher

导入依赖

dependencies {
  compile("com.github.hotchemi:permissionsdispatcher:${latest.version}") {
      // if you don't use android.app.Fragment you can exclude support for them
      exclude module: "support-v13"
  }
  annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:${latest.version}"
}

具体他版本请参考github库

###如何使用

此图片来自网络

上述图片来自博客:http://blog.csdn.net/u012939909/article/details/53896357

发现是通过注解的形式来完成的。
####(1)@RuntimePermissions
需要你添加在需要申请的Activity或者fragment上
####(2)@NeedsPermission
比如方法needCamrea()方法里面需要完成调用相机拍照功能,那么就要在此方法上添加 这个注解
####(3)@OnShowRationale
需要你自己定义一个方法,这个方法的作用是向用户解释为啥需要这个权限,当然了只有第一次请求权限被用户拒绝了,下次请求权限之前会调用
####(4)@OnPermissionDenied
当用户拒绝了权限请求时候,需要告诉用户为什么的方法上
####(5)@OnNeverAskAgain
当用户选中了授权窗口中不在询问的复选框之后并且拒绝了权限请求时候调用的方法,一般可以向用户解释为何申请,或者在此弹出权限请求对话框。

##例子

@RuntimePermissions
public class MainActivity extends AppCompatActivity{
@NeedsPermission(Manifest.permission.CAMERA)
void showCamera() {
    getSupportFragmentManager().beginTransaction()
            .replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance())
            .addToBackStack("camera")
            .commitAllowingStateLoss();
}
//此方法需要权限,只有赋予了权限之后,这个方法再回去执行
@OnShowRationale(Manifest.permission.CAMERA)
void showRationaleForCamera(final PermissionRequest request) {
    new AlertDialog.Builder(this)
        .setMessage(R.string.permission_camera_rationale)
        .setPositiveButton(R.string.button_allow, (dialog, button) -> request.proceed())
        .setNegativeButton(R.string.button_deny, (dialog, button) -> request.cancel())
        .show();
}

@OnPermissionDenied(Manifest.permission.CAMERA)
void showDeniedForCamera() {
    Toast.makeText(this, R.string.permission_camera_denied, Toast.LENGTH_SHORT).show();
}

@OnNeverAskAgain(Manifest.permission.CAMERA)
void showNeverAskForCamera() {
    Toast.makeText(this, R.string.permission_camera_neverask, Toast.LENGTH_SHORT).show();
}

然后,你需要编译Build一下,然后会生成这个类的专属 Dispatcher,然后在调用

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    findViewById(R.id.button_camera).setOnClickListener(v -> {
      // NOTE: delegate the permission handling to generated method
      MainActivityPermissionsDispatcher.showCameraWithPermissionCheck(this);
    });
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    // NOTE: delegate the permission handling to generated method
    MainActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);

}

##用户拒绝了权限,且不再询问
在调用 requestPermissions() 后,onRequestPermissionsResult() 会立刻被调用并且申请结果为 PERMISSION_DENIED 。系统默认的就返回不通过

那么只能让应用跳转到权限设置界面,然后让用户手动开启
可以先自定义弹窗,询问用户是否开启权限,允许之后:

Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);

Uri uri = Uri.fromParts("package", getPackageName(), null);

intent.setData(uri);

startActivityForResult(intent, REQUEST_PERMISSION_SETTING);

采用上述办法,跳转到权限设置的界面,然后让用户手动给予这个应用权限

##第三方权限库RxPermission
https://github.com/tbruyelle/RxPermissions
专门配合RxJava思想

示例写法

   if ((ContextCompat.checkSelfPermission(GudleActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {

        } else {
            ActivityCompat.requestPermissions(GudleActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
        }
        if ((ContextCompat.checkSelfPermission(GudleActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)) {

        } else {
            ActivityCompat.requestPermissions(GudleActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
        }
        if ((ContextCompat.checkSelfPermission(GudleActivity.this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED)) {

        } else {
            ActivityCompat.requestPermissions(GudleActivity.this, new String[]{Manifest.permission.READ_PHONE_STATE}, 2);
        }
        if ((ContextCompat.checkSelfPermission(GudleActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {

        } else {
            ActivityCompat.requestPermissions(GudleActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 3);
        }

对于结果处理

  switch (requestCode) {
            case 0:
                Log.d("xlj","得到的数据是:"+grantResults[0]+"对应的权限是"+permissions[0]);
                break;
            case 1:
                Log.d("xlj","得到的数据是:"+grantResults[0]+"对应的权限是"+permissions[0]);
                break;
            case 2:
                Log.d("xlj","得到的数据是:"+grantResults[0]+"对应的权限是"+permissions[0]);
                break;
            case 3:
                Log.d("xlj","得到的数据是:"+grantResults[0]+"对应的权限是"+permissions[0]);
                break;
        }

则会报数组越界异常

发布了28 篇原创文章 · 获赞 4 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章