Android的权限机制在第一个版本的时候就已经有了,但是因为有许多软件在安装时候就申请了各种权限,若用户不同意就安装不了,所以从结果上来说,权限机制并不能很好地保护用户的安全和隐私。为此Android开发团队在Android6.0版本中引进了运行时权限的机制。

运行时权限机制其实就是用户不需要在安装软件的时候就获取所有的权限,而是可以在软件的某个功能需要用到这些权限的时候再向用户申请。比如一个软件里面有一个点击按钮实现去电的功能,但是并不是第一次打开软件的时候就需要用到这个权限,这时就可以使用运行时权限机制,在用户需要用到相关功能的时候再向用户申请获取权限。这样就能很好地避免了你不喜欢软件申请的某个权限(不授权安装不了)但是你又不得不用这个软件的情况的出现。

实例:点击按钮实现去电10086

首先在界面中设置一个Button

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/make_call"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="CALL 10086" />
</LinearLayout>

然后在AndroidManifest中的<manifest>标签里添加申请拨打电话的权限:

<uses-permission android:name="android.permission.CALL_PHONE" />

最后在Activity中对按下按钮的操作进行设置

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button makeCall = (Button) findViewById(R.id.make_call);
        makeCall.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //检查用户是否授权权限
                //PERMISSION_GRANTED值为0,PERMISSION_DENIED值为-1
                //不等于PERMISSION_GRANTED的话就是未授权
                if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.
                        permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { 
                    //如果第一次用户拒绝授权,那么下次需要申请权限的时候可以显示说明(可要可不要) 
                    if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,  
                    Manifest.permission.CALL_PHONE)) {
                        Toast.makeText(MainActivity.this, 
                            "You should allow this grant because we should get this permission to make a call!", 
                            Toast.LENGTH_SHORT).show();
                    }
                    //向用户申请权限,最后一个参数requestcode对应回调函数onRequestPermissionsResult里的requestcode.
                    ActivityCompat.requestPermissions(MainActivity.this, new
                            String[]{ Manifest.permission.CALL_PHONE },1);  
                } else {
                    call();
                }
            }
        });
    }

    private void call() {
        try {
            Intent intent = new Intent(Intent.ACTION_CALL);
            intent.setData(Uri.parse("tel:10086"));
            startActivity(intent);
        } catch (SecurityException s) {
            s.printStackTrace();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    call();
                } else {
                    Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
        }
    }

}

上面的代码包含了运行时权限所有的操作。首先在用户点击按钮之后,程序会通过ContextCompat.checkSelfPermission() 方法来检测用户是否授权了程序打电话的权限。

ContextCompat.checkSelfPermission() 方法主要用于检测某个权限是否已经被授予。checkSelfPermission()方法需要传递两个参数,第一个参数需要传入Context,第二个参数需要传入需要检测的权限,如打电话的权限:Manifest.permission.CALL_PHONE。方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。若返回值为GRANTED则为已授权,否则就需要进行申请授权了。

ActivityCompat.shouldShowRequestPermissionRationale()方法用于解释权限,如果第一次申请权限被用户拒绝后,我们可以在第二次申请权限的时候进行一个使用该权限的说明,权限说明的逻辑写在方法内部。shouldShowRequestPermissionRationale()方法接收两个参数,第一个参数接收Activity对象,第二个参数则接收一个String类型的权限参数。

关于这个方法,查看官方文档后总结得出:

ActivityCompat.shouldShowRequestPermissionRationale(Activity activity, String permission)

1、在Android6.0系统以下(小于SDK23) 返回false;
2、曾经申请过权限被拒绝,再次申请的时候 返回true;
3、系统设置中禁止该程序的权限授权 返回false;
4、在申请权限的时候用户选拒绝并不再提醒的时候 返回false.

ActivityCompat.requestPermissions()方法则是用来向用户申请授权的。requestPermissions()方法需要接受三个参数,第一个参数是Context,要求是Activity的实例,这里传入MainAcitivty.this指向当前Activity;第二个参数是需要申请的权限名的String数组,在数组中填好权限名即可;第三个参数是权限申请的请求码requestcode,需要填一个唯一值,对应后面回调onRequestPermissionsResult()方法的第一个参数requestcode。从第二个参数是一个String数组可以看出,系统支持一次性申请多个权限,然后系统会依次弹出对话框询问用户是否授予相应权限。

在运行了requestPermissions()方法之后,系统会弹出一个申请权限的对话框:如图一

 


Android运行时权限 拒绝不再询问 安卓运行时权限_android

图一

在调用 requestPermissions()方法之后,用户选择是/否允许申请权限的结果会被封装到grantResults参数中并回调onRequestPermissionsResult()方法。在onRequestPermissionsResult()方法中,首先会通过requestCode定位到你的申请,然后验证grantResults数组中对应申请的结果。这里的grantResults数组对应requestPermissions()方法申请权限时第二个权限字符串数组,如果同时申请两个权限,那么grantResults的length就为2,分别记录着两个权限的申请结果。

onRequestPermissionsResult()方法中也分为两种情况,若用户同意权限申请,那么程序便执行拨打电话的操作,若用户不同意,那么不会执行拨打电话的操作并发出提示。

在Android6.0以前,若要申请危险权限并不需要另外处理,只是需要和普通权限一样,在AndroidManifest.xml中声明权限即可:

<!-- 申请拨打电话权限 -->
<uses-permission android:name="android.permission.CALL_PHONE" />

拓展知识点:

Android将将所有权限归为两类,一类是普通权限,一类是危险权限。普通权限一般不会威胁到用户的安全和隐私,对于这部分权限,系统自动对软件进行授权,不需要询问用户;而危险权限则是可能会对用户隐私和设备安全造成影响的权限,如获取设备地理位置、获取设备联系人信息等,这些就需要明确通知用户,并由用户手动进行授权才可以进行相应操作。

Android中的权限有上百种,但是我们只需要区分其中的危险权限即可。Android中的危险权限一共是九组24个,其余的都是普通权限。

危险权限

权限组名

权限名

CALENDAR(日历)

READ_CALENDAR

WRITE_CALENDAR

CAMERA(摄像头)

CAMERA

CONTACTS(联系人)

READ_CONTACTS

WRITE_CONTACTS

GET_ACCOUNTS

LOCATION(定位)

ACCESS_FINE_LOCATION

ACCESS_COARSE_LOCATION

MICROPHONE(麦克风)

RECORD_AUDIO

PHONE(手机)

READ_PHONE_STATE

CALL_PHONE

READ_CALL_LOG

WRITE_CALL_LOG

ADD_VOICEMAIL

USE_SIP

PROCESS_OUTGOING_CALLS

SENSOR(传感器)

BODY_SENSORS

SMS(短信)

SEND_SMS

RECEIVE_SMS

READ_SMS

RECEIVE_WAP_PUSH

RECEIVE_MMS

STORAGE(存储)

READ_EXTERNAL_STORAGE

WRITE_EXTERNAL_STORAGE

 在我们需要使用权限的时候,如果只是需要使用普通权限,那么只需要在AndroidManifest.xml中声明即可;如果需要使用危险权限,那么就需要像上文中的那样进行进行时权限处理。

另外,需要注意的是,我们在进行运行时权限处理的时候,虽然申请的是单一的权限,但是一旦用户同意授权之后,这个权限所属的权限组中其他的权限同时也会被授权了。

精简记忆版:

在点击按钮或其他事件触发后,首先判断ContextCompat.checkSelfPermission(MainActivity.this, Manifest. permission.CALL_PHONE) 是否等于 PackageManager.PERMISSION_GRANTED,不等于则未授权该权限,需要通过 ActivityCompat.requestPermissions(MainActivity.this, new String[]{ Manifest.permission.CALL_PHONE },1)来申请权限,然后会回调onRequestPermissionsResult方法,根据requestCode来进行相应的逻辑处理。