权限

Android - 6.0动态权限申请封装

2018-08-21  本文已影响574人  卖火柴的小女孩_f10c

运行时权限

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

对于以 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 我就不解释了.有兴趣的就自己去百度。

上一篇下一篇

猜你喜欢

热点阅读