美文网首页权限
Android - 6.0动态权限申请封装

Android - 6.0动态权限申请封装

作者: 卖火柴的小女孩_f10c | 来源:发表于2018-08-21 17:03 被阅读574次

    运行时权限

    此版本引入了一种新的权限模式,如今,用户可直接在运行时管理应用权限。这种模式让用户能够更好地了解和控制权限,同时为应用开发者精简了安装和自动更新过程。用户可为所安装的各个应用分别授予或撤销权限。

    对于以 Android 6.0(API 级别 23)或更高版本为目标平台的应用,请务必在运行时检查和请求权限。要确定您的应用是否已被授予权限,请调用新增的 checkSelfPermission() 方法。要请求权限,请调用新增的 requestPermissions() 方法。即使您的应用并不以 Android 6.0(API 级别 23)为目标平台,您也应该在新权限模式下测试您的应用。

    如需了解有关在您的应用中支持新权限模式的详情,请参阅使用系统权限。如需了解有关如何评估新模式对应用的影响的提示,请参阅权限最佳做法

    新增检查方法 checkSelfPermission()

    | 参数 |
    permission String:正在检查的权限的名称。
    这个值绝对不能null。
    | 返回 |
    | int | PackageManager.PERMISSION_GRANTED如果您有权限,或者如果没有。PackageManager.PERMISSION_DENIED
    价值是PERMISSION_GRANTED或PERMISSION_DENIED。

    新增请求权限方法 requestPermissions()

    | 参数 |
    permissions String:请求的权限。我必须非空并且不是空的。
    requestCode int:特定于应用程序的请求代码以与报告的结果匹配。应该>=0onRequestPermissionsResult(int, String[], int[])
    | 抛出 |
    IllegalArgumentException 如果requestCode为负数。

    官方有个地方说可以使用ContextWrapper.checkSelfPermission(String) 。其实这个方法很多地方都能使用到。
    好,Android M 为什么会新增这两方法呢.因为在Android 6.0 (API 23) 开始用户开始在应用运行时向其授予权限,而不是在应用安装时授予。这种权限机制可以让用户更好的管理应用的权限,保障用户隐私。从此之后...

    系统权限分为两种

    正常权限

    不会直接给用户隐私权带来风险。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。
    android.permission.ACCESS LOCATIONEXTRA_COMMANDS
    android.permission.ACCESS NETWORKSTATE
    android.permission.ACCESS NOTIFICATIONPOLICY
    android.permission.ACCESS WIFISTATE
    android.permission.ACCESS WIMAXSTATE
    android.permission.BLUETOOTH
    android.permission.BLUETOOTH_ADMIN
    android.permission.BROADCAST_STICKY
    android.permission.CHANGE NETWORKSTATE
    android.permission.CHANGE WIFIMULTICAST_STATE
    android.permission.CHANGE WIFISTATE
    android.permission.CHANGE WIMAXSTATE
    android.permission.DISABLE_KEYGUARD
    android.permission.EXPAND STATUSBAR
    android.permission.FLASHLIGHT
    android.permission.GET_ACCOUNTS
    android.permission.GET PACKAGESIZE
    android.permission.INTERNET
    android.permission.KILL BACKGROUNDPROCESSES
    android.permission.MODIFY AUDIOSETTINGS
    android.permission.NFC
    android.permission.READ SYNCSETTINGS
    android.permission.READ SYNCSTATS
    android.permission.RECEIVE BOOTCOMPLETED
    android.permission.REORDER_TASKS
    android.permission.REQUEST INSTALLPACKAGES
    android.permission.SET TIMEZONE
    android.permission.SET_WALLPAPER
    android.permission.SET WALLPAPERHINTS
    android.permission.SUBSCRIBED FEEDSREAD
    android.permission.TRANSMIT_IR
    android.permission.USE_FINGERPRINT
    android.permission.VIBRATE
    android.permission.WAKE_LOCK
    android.permission.WRITE SYNCSETTINGS
    com.android.alarm.permission.SET_ALARM
    com.android.launcher.permission.INSTALL_SHORTCUT
    com.android.launcher.permission.UNINSTALL_SHORTCUT

    危险权限

    会授予应用访问用户机密数据的权限。如果您列出了危险权限,则用户必须明确批准您的应用使用这些权限。
    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

    阅读到这里,小伙伴会有疑问.为什么危险权限一组一组。这里解释一下,比如 permission-group.SMS 中的其中一条READ_SMS被授权了。那么在使用其他同组权限的时候就不会去向用户发起请求,我们使用checkSelfPermission得到的结果也是授权的。

    兼容性的问题

    在Android 6.0 以前只要在AndroidManifest.xml上申明了权限,应用就默认带有权限.(在原生系统中)。有些厂商的手机一样可以让用户关闭掉权限。那么...在API<23的情况下关掉权限.再使用权限.那么只有死路条.所以。采取良好的方案就是Try 。

    封装思路

    其实每写一篇简书前我都会看其他人写的文章,这样就可以把每个人的优势结合在一起。我之前做适配的时候也在Github上找过动态权限申请的库,也可以说成是工具类把。我去看了源码。又一个声称可以在任何地方都可以使用到权限,后来我就用上了这个。其实当时我并不是很理解,因为权限当时我去找资料了是Activity才能发起了.回调固然回到Activity,后来我翻看他的源码...是new 了一个Activity出来.还有一些工具类是让你一行代码调用.然后在onRequestPermissionsResult也要写一行工具类的代码.我觉得这种比较麻烦。后来我就想到了。将使用到的方法功能封装在一个Activity里面。让BaseActivity去继承这个PermissionRequestActivity。一个方法使用,给一个回调出来,授权权限是否成功。

    PermissionRequestActivity

    
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.net.Uri;
    import android.os.Build;
    import android.provider.Settings;
    import android.support.annotation.NonNull;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.app.FragmentActivity;
    import android.support.v7.app.AlertDialog;
    
    /**
     * Created by 卖火柴的小女孩 - Jc on 2018/8/21.
     */
    
    public class PermissionRequestActivity extends FragmentActivity {
    
        private static final int PERMISSION_REQUEST_CODE = 1088;
    
        private String mPermissionDes;
        private CallBack mCallBack;
    
    
        /**
         * 权限申请使用方法
         *
         * @param permissionDes 权限说明
         * @param callBack      申请回调
         * @param permissions   申请权限
         */
        protected void requestPermission(String permissionDes, CallBack callBack, @NonNull String... permissions) {
            mCallBack = callBack;
            mPermissionDes = permissionDes;
            if (checkPermission(permissions))
                mCallBack.hasPermission();
            else {
                ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
            }
        }
    
        /**
         * 判断系统版本大于6.0的时候
         *
         * @param permissions 申请权限
         * @return
         */
        protected boolean checkPermission(@NonNull String... permissions) {
            //大于6.0的时候需要动态申请权限.小于6.0的时候如果用户手动关闭权限,程序即崩 需要做Try处理
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                return checkSelfPermissions(permissions);
            }
            return true;
        }
    
        /**
         * 检查权限是否已经授权
         *
         * @param permissions 申请权限
         * @return
         */
        private boolean checkSelfPermissions(@NonNull String... permissions) {
            boolean flag = true;
            for (String p : permissions) {
                if (ActivityCompat.checkSelfPermission(this, p) != PackageManager.PERMISSION_GRANTED) {
                    flag = false;
                    break;
                }
            }
            return flag;
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            boolean hasAllGranted = true;
            for (int i = 0; i < grantResults.length; ++i) {
                if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                    hasAllGranted = false;
                    if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) {
                        showDialogPrompt();
                    } else {
                        //权限申请被拒绝 ,但用户未选择'不再提示'选项
                        mCallBack.lossPermission();
                    }
                    break;
                }
            }
            if (hasAllGranted) {
                mCallBack.hasPermission();
            }
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    
        /**
         * 由于用户手动关闭权限提示。APP需要做人性化提示
         */
        private void showDialogPrompt() {
            new AlertDialog.Builder(this)
                    .setTitle("权限申请")
                    .setMessage(mPermissionDes)
                    .setCancelable(false)
                    .setPositiveButton("去设置", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            //引导用户至设置页手动授权
                            getAppSetting();
                        }
                    })
                    .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            //引导用户手动授权,权限请求失败
                            mCallBack.lossPermission();
                        }
                    }).show();
        }
    
        /**
         * 跳转设置 应用设置界面
         *
         * @return
         */
        private Intent getAppSetting() {
            Intent localIntent = null;
            if (Build.VERSION.SDK_INT >= 9) {
                localIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                localIntent.setData(Uri.fromParts("package", getPackageName(), null));
                localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            } else if (Build.VERSION.SDK_INT <= 8) {
                localIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                localIntent.setAction(Intent.ACTION_VIEW);
                localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
                localIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());
                localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
            startActivity(localIntent);
            return localIntent;
        }
    
    
        /**
         * 权限申请回调
         */
        interface CallBack {
            void hasPermission();
    
            void lossPermission();
        }
    
    }
    

    使用

    requestPermission("获取手机信息-获取手机号码、IMEI、IMSI权限\n读写手机存储-读写手机存储", new CallBack() {
                    @Override
                    public void hasPermission() {
                        String IMEI = getIMEI(MainActivity.this);
                        ((TextView) findViewById(R.id.tv)).setText(IMEI);
                    }
    
                    @Override
                    public void lossPermission() {
                        Toast.makeText(MainActivity.this, "权限申请被拒绝", Toast.LENGTH_SHORT).show();
                    }
                }, Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_EXTERNAL_STORAGE);
    

    代码讲解

    其实代码不多,只有100多行。
    方法: requestPermission
    参数:String permissionDes -- 用于用户选择'不再提示'按钮提示语
    CallBack callBack -- 应用是否带有权限或是否授权权限成功
    @NonNull String... permissions -- 需要授权或检查的权限

    这个方法一进去 我做了一个判断 checkPermission(permissions) , 先去检查是否有权限,如果有权限的话.那么就不去授权权限了.

    方法 :checkPermission() 进来判断是否有权限的时候这个方法有一个API 就是刚才最上面开始讲的checkSelfPermission 这个需要 Build.VERSION.SDK_INT >= Build.VERSION_CODES.M..所以不满足这个条件的都以TRUE返回出去,表明应用在6.0以下的设备默认有这些权限。但是刚才在兼容性的时候讲过了.会崩。

    接下来检测到没有权限,使用了这一段代码

    ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
    

    这一行就是向用户发起权限授权的代码了.接下来 我们看 onRequestPermissionsResult 。 这个方法是向用户请求权限的回调结果。这个方法里面我做了处理,我检测了用户是否点击了 ' 不再提示 ' 的按钮。(有些手机没有这个按钮,有些手机有,当选择了此按钮之后 再次发起授权请求,系统将不再向用户发起权限授权请求.而是直接返回拒绝的信息回来.)那么这时我们就需要做一个友好的提示了。如果用户是由于手动点击了这个不再提示的按钮而导致第二次或第n次没有收到授权的消息.那我们应用需要给用户一个提示框showDialogPrompt(),我这里写了一个最简单的弹窗。很多APP都有自己主题的弹窗.主要就是提示用户。告知用户 他的权限被他自己手动不再提示了.那么我们给他一个系统设置界面的跳转getAppSetting(); 然后这个方法的Build.VERSION.SDK_INT <= 8 我就不解释了.有兴趣的就自己去百度。

    相关文章

      网友评论

        本文标题:Android - 6.0动态权限申请封装

        本文链接:https://www.haomeiwen.com/subject/crvdiftx.html