Android权限源码及流程解析

来源:互联网 发布:网络优化工程师招聘 编辑:程序博客网 时间:2024/06/08 16:28

简介

在Android应用开发过程中,经常会向用户申请获得手机的一些权限,以提升应用的交互友好性(如通讯录权限),有时候这些权限甚至是必不可少的(如连接网络等)。本篇博客就将对Android应用开发中的权限管理进行一定的探究与分析。


初识

在Android Studio中通过创建Login Activity,可以获得系统自动为我们编写好的权限申请代码,如下所示:

/** * Id to identity READ_CONTACTS permission request. */private static final int REQUEST_READ_CONTACTS = 0;private boolean mayRequestContacts() {    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {        return true;    }    if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {        return true;    }    if (shouldShowRequestPermissionRationale(READ_CONTACTS)) {        Snackbar.make(mEmailView, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)                .setAction(android.R.string.ok, new View.OnClickListener() {                    @Override                    @TargetApi(Build.VERSION_CODES.M)                    public void onClick(View v) {                        requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);                    }                });    } else {        requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);    }    return false;}/** * Callback received when a permissions request has been completed. */@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,                                       @NonNull int[] grantResults) {    if (requestCode == REQUEST_READ_CONTACTS) {        if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {            populateAutoComplete();        }    }}

故名思议,该代码申请的权限为REQUEST_READ_CONTACTS,即为申请阅读联系人,用于在登录过程中可以访问通讯录直接填写登录信息。
来分析这段代码,通过调用mayRequestContacts进入权限申请流程,该方法中由三个if语句构成:

  • if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)这个if语句是用来判断当前Android版本是否小于系统所定义的版本代号为M的Android版本,即Android 6.0,对应API为23)。当小于这个版本时会返回true,即权限获得。这一条判断是由于Android版本不同时申请权限方式不同造成的,我们之后再来分析。
  • if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED)这个if语句很明显,是用来判断是否已经对应用授权了读取通讯录的权限,如果已经获取,则返回true。
  • if (shouldShowRequestPermissionRationale(READ_CONTACTS))这个if语句才是获得授权的主体,也是我们主要分析的对象。

深入

shouldShowRequestPermissionRationale(READ_CONTACTS)

shouldShowRequestPermissionRationale是系统自带的方法,获取它的具体实现:

/** * 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 permission A permission your app wants to request. * @return Whether you can show permission rationale UI. * * @see #checkSelfPermission(String) * @see #requestPermissions(String[], int) * @see #onRequestPermissionsResult(int, String[], int[]) */public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {    return getPackageManager().shouldShowRequestPermissionRationale(permission);}

可以看到,该方法又调用了另一个方法,我们在此不再深究,着重看该方法的注释。注释中说,该方法当且仅当应用需要申请权限,且所申请的权限用户不一定可以理解原因的时候才需要调用,调用的目的是确定开发者需要展示一个可视化说明来请求权限。
READ_CONTACTS是系统定义的一个字符串常量android.permission.READ_CONTACTS


Snackbar.make

当确定需要一个可视化说明后,则创建一个Snackbar。
Snackbar是Android Support Design Library库中的一个控件,可以在屏幕底部快速弹出消息,比Toast更加好用,感兴趣的读者可以进一步了解。

Snackbar.make(mEmailView, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)                    .setAction(android.R.string.ok, new View.OnClickListener() {                        @Override                        @TargetApi(Build.VERSION_CODES.M)                        public void onClick(View v) {                            requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);                        }                    });

可以看到,当点击OK(即android.R.string.ok定义的字符串)后会调用requestPermissions方法,这个便是赋予应用权限的方法。
值得一提的是,当该权限不需要可视化权限的时候(即shouldShowRequestPermissionRationale返回false),会直接调用requestPermissions方法。


requestPermissions

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;}

该方法的第一个if语句是用来保证在同一时间,只能允许一组(不是一个)权限,之后会创建一个Intent,并开始对这一组权限进行授权。


onRequestPermissionsResult

回到第一段代码,onRequestPermissionsResult是一个回调函数,当requestPermissions完成授权之后会调用这个方法,该方法接收三个参数,分别为int requestCode,@NonNull String[] permissions,@NonNull int[] grantResults。

  • requestCode:在requestPermissions中我们传入了这个参数,它是用来在onRequestPermissionsResult方法中与我们事先定义好的请求代号匹配,以确保的确是该请求被同意并完成授权;
  • permissions:同样是在requestPermissions传入的参数,是系统定义的该组权限的字符串格式的名称;
  • grantResults:请求结果。

在方法体中:

if (requestCode == REQUEST_READ_CONTACTS) {    if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {        populateAutoComplete();    }}

第一个if语句是用来检测onRequestPermissionsResult处理的请求是否为我们定义的读取通讯录请求;
第二个if语句是用来检测请求结果是否为授予权限,其中第一个条件是该组请求只包含一个请求,第二个条件是该组请求的第一个请求的处理结果是授予权限
通过这两个if判断后,便可以继续获取权限之后的代码了。


补充

之前有提到,当当前Android版本小于系统所定义的版本代号为M的Android版本时,是会直接返回true而不继续执行接下来的请求权限代码的。这是因为在之前,Android采取的是安装时授权,即当安装应用程序时,会出现向用户请求权限的界面,用户确认之后才可以开始安装,而在实际情况中,用户通常都不会仔细查看这些权限,导致了用户手机的安全隐患,因此从Android 6.0(即版本代号M)开始,Android便更改为运行时授权,即当应用程序运行且运行到需要该权限的时候,才会向用户请求权限。这一方面保证了用户手机的安全性,同时也实现了当用户不对某一请求授权时,只会影响一部分功能,而不会影响其它功能的使用。

老版本请求权限方式

当Android版本低于Android 6.0时,授权方式非常简单,只需要在AndroidManifest.xml中添加对应的权限声明即可。如:

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

这样即可完成读取通讯录权限的授予。

原创粉丝点击