当前位置: 首页 > news >正文

Android6.0M权限管理实战,完美轻量级封装

为什么80%的码农都做不了架构师?>>>   hot3.png

本文出自【DylanAndroid的博客】

Android6.0M权限管理实战,完美轻量级封装

随着Android版本的不断更新,Android再权限管理方面的诟病越来越明显。Google的Android开发人员也意识到了Android应用在权限管理方面的各种问题,让好多用户摸不着头脑就使用了用户的隐私数据。
为了在权限这方面加强管理,给用户一个比较好的体验。Android 6.0代号M的发布彻底解决了这一问题,取而代之的做法是这样的:
app在运行时逐个询问用户授予权限。Android 6.0(api23)M系统中,做了一些限制, 开发者在使用到每条权限时必须自己调用相关代码请求用户授予权限。如果没有获得某项权限,直接使用相关功能,则会导致自己程序crash.。为了解决这方面的问题,我们每次请求某一个权限要写好多代码解决这个问题,非常的麻烦。
下面是我做的一个轻量级的封装,可以减少很多重复的工作。

效果图
请求权限

25134503_QWY3.png

用户禁止了权限

25134513_mwTj.png

一.Android6.0M对权限的划分

Android6.0M中对用户的权限分为了一般权限和危险权限,这些危险权限除了在AndroidManifest.xml中注册以外,还需要在使用的时候对用户进行请求权限弹窗提醒,才可以使用。
这些危险权限如下:
25134525_CZLN.png

二.封装一个MPermissionsActivity的思路和步骤

  • 第一步:检测所有的权限是否都已授权
   /** * 检测所有的权限是否都已授权 * * @param permissions * @return */
     private boolean checkPermissions(String[] permissions) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
             return true;
         }

         for (String permission : permissions) {
             if (ContextCompat.checkSelfPermission(this, permission) !=
                     PackageManager.PERMISSION_GRANTED) {
                 return false;
             }
         }
         return true;
     }
  • 第二步:获取权限集中需要申请权限的列表
 /** * 获取权限集中需要申请权限的列表 * * @param permissions * @return */
    private List<String> getDeniedPermissions(String[] permissions) {
        List<String> needRequestPermissionList = new ArrayList<>();
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(this, permission) !=
                    PackageManager.PERMISSION_GRANTED ||
                    ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
                needRequestPermissionList.add(permission);
            }
        }
        return needRequestPermissionList;
    }
  • 第三步:请求权限
  /** * 请求权限 * * @param permissions 请求的权限 * @param requestCode 请求权限的请求码 */
    public void requestPermission(String[] permissions, int requestCode) {
        this.REQUEST_CODE_PERMISSION = requestCode;
        if (checkPermissions(permissions)) {
            permissionSuccess(REQUEST_CODE_PERMISSION);
        } else {
            List<String> needPermissions = getDeniedPermissions(permissions);
            ActivityCompat.requestPermissions(this, needPermissions.toArray(new String[needPermissions.size()]), REQUEST_CODE_PERMISSION);
        }
    }
  • 第四步:处理权限请求回调
  /** * 系统请求权限回调 * * @param requestCode * @param permissions * @param grantResults */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE_PERMISSION) {
            if (verifyPermissions(grantResults)) {
                permissionSuccess(REQUEST_CODE_PERMISSION);
            } else {
                permissionFail(REQUEST_CODE_PERMISSION);
                showTipsDialog();
            }
        }
    }
  • 第五步:查看处理权限请求回调用户是否已经授权
 /** * 确认所有的权限是否都已授权 * * @param grantResults * @return */
    private boolean verifyPermissions(int[] grantResults) {
        for (int grantResult : grantResults) {
            if (grantResult != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }
  • 第六步:授权成功处理函数
  /** * 获取权限成功 * * @param requestCode */
    public void permissionSuccess(int requestCode) {
        Log.d(TAG, "获取权限成功=" + requestCode);

    }
  • 第七步:授权失败处理函数与弹出用户提示

    /** * 权限获取失败 * @param requestCode */
    public void permissionFail(int requestCode) {
        Log.d(TAG, "获取权限失败=" + requestCode);
    }

    /** * 显示提示对话框 */
    private void showTipsDialog() {
        new AlertDialog.Builder(this)
                .setTitle("提示信息")
                .setMessage("当前应用缺少必要权限,该功能暂时无法使用。如若需要,请单击【确定】按钮前往设置中心进行权限授权。")
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                })
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        startAppSettings();
                    }
                }).show();
    }
  • 第八步:授权失败给用户提示后想再次开启跳到设置app权限界面
    /** * 启动当前应用设置页面 */
    private void startAppSettings() {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.parse("package:" + getPackageName()));
        startActivity(intent);
    }

三.完整的MPermissionsActivity代码

package cn.bluemobi.dylan.mpermissions;

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.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

/** * Created by yuandl on 2016-11-16. */

public class MPermissionsActivity extends AppCompatActivity {
    private final String TAG = "MPermissions";
    private int REQUEST_CODE_PERMISSION = 0x00099;

    /** * 请求权限 * * @param permissions 请求的权限 * @param requestCode 请求权限的请求码 */
    public void requestPermission(String[] permissions, int requestCode) {
        this.REQUEST_CODE_PERMISSION = requestCode;
        if (checkPermissions(permissions)) {
            permissionSuccess(REQUEST_CODE_PERMISSION);
        } else {
            List<String> needPermissions = getDeniedPermissions(permissions);
            ActivityCompat.requestPermissions(this, needPermissions.toArray(new String[needPermissions.size()]), REQUEST_CODE_PERMISSION);
        }
    }

    /** * 检测所有的权限是否都已授权 * * @param permissions * @return */
    private boolean checkPermissions(String[] permissions) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return true;
        }

        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(this, permission) !=
                    PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    /** * 获取权限集中需要申请权限的列表 * * @param permissions * @return */
    private List<String> getDeniedPermissions(String[] permissions) {
        List<String> needRequestPermissionList = new ArrayList<>();
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(this, permission) !=
                    PackageManager.PERMISSION_GRANTED ||
                    ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
                needRequestPermissionList.add(permission);
            }
        }
        return needRequestPermissionList;
    }


    /** * 系统请求权限回调 * * @param requestCode * @param permissions * @param grantResults */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE_PERMISSION) {
            if (verifyPermissions(grantResults)) {
                permissionSuccess(REQUEST_CODE_PERMISSION);
            } else {
                permissionFail(REQUEST_CODE_PERMISSION);
                showTipsDialog();
            }
        }
    }

    /** * 确认所有的权限是否都已授权 * * @param grantResults * @return */
    private boolean verifyPermissions(int[] grantResults) {
        for (int grantResult : grantResults) {
            if (grantResult != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    /** * 显示提示对话框 */
    private void showTipsDialog() {
        new AlertDialog.Builder(this)
                .setTitle("提示信息")
                .setMessage("当前应用缺少必要权限,该功能暂时无法使用。如若需要,请单击【确定】按钮前往设置中心进行权限授权。")
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                })
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        startAppSettings();
                    }
                }).show();
    }

    /** * 启动当前应用设置页面 */
    private void startAppSettings() {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.parse("package:" + getPackageName()));
        startActivity(intent);
    }

    /** * 获取权限成功 * * @param requestCode */
    public void permissionSuccess(int requestCode) {
        Log.d(TAG, "获取权限成功=" + requestCode);

    }

    /** * 权限获取失败 * @param requestCode */
    public void permissionFail(int requestCode) {
        Log.d(TAG, "获取权限失败=" + requestCode);
    }
}

四.用法

  • 布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="cn.bluemobi.dylan.mpermissions.MainActivity">

    <TextView  android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" />

    <Button  android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onClick1" android:text="打电话" />

    <Button  android:id="@+id/button2" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onClick2" android:text="写SD卡" />

    <Button  android:id="@+id/button3" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onClick3" android:text="拍照" />
</LinearLayout>
  • MainActivity中使用:继承MPermissionsActivity即可
package cn.bluemobi.dylan.mpermissions;

import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends MPermissionsActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /** * 打电话 * * @param view */
    public void onClick1(View view) {
        requestPermission(new String[]{Manifest.permission.CALL_PHONE}, 0x0001);
    }

    /** * 写SD卡 * * @param view */
    public void onClick2(View view) {
        requestPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x0002);
    }

    /** * 拍照 * * @param view */
    public void onClick3(View view) {
        requestPermission(new String[]{Manifest.permission.CAMERA}, 0x0003);
    }

    /** * 权限成功回调函数 * * @param requestCode */
    @Override
    public void permissionSuccess(int requestCode) {
        super.permissionSuccess(requestCode);
        switch (requestCode) {
            case 0x0001:
                Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:13468857714"));
                startActivity(intent);
                break;
        }

    }

}

五.GitHub

转载于:https://my.oschina.net/dccjll/blog/886706

相关文章:

  • AX_xSession
  • CCF NOI1049 旋转图像
  • radio checkbox 修改默认样式
  • CentOS搭建FTP
  • 黑马程序员 九、IO 操作(1)
  • 运行第一个Docker容器-Docker for Web Developers(1)
  • Linux 虚拟机--KVM安装和配置
  • 微软云Linux服务器 Mysql、tomcat远程连接错误解决办法
  • 认识htnl最基本的几个标签
  • ES6学习记录-let和var的区别
  • Java 集合类 List Set Map 哪些线程安全
  • 安装Python的机器学习包Sklearn 出错解决方法
  • 【干货】机器学习常见算法分类汇总
  • 《Selenium自动化测试指南》目录—导读
  • 一次耐人寻味的SQL优化:除了SQL改写,还要考虑什么?
  • express如何解决request entity too large问题
  • Java教程_软件开发基础
  • js正则,这点儿就够用了
  • leetcode388. Longest Absolute File Path
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • MySQL几个简单SQL的优化
  • Otto开发初探——微服务依赖管理新利器
  • Webpack4 学习笔记 - 01:webpack的安装和简单配置
  • Webpack入门之遇到的那些坑,系列示例Demo
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 新版博客前端前瞻
  • 在Docker Swarm上部署Apache Storm:第1部分
  • Prometheus VS InfluxDB
  • 湖北分布式智能数据采集方法有哪些?
  • ​ArcGIS Pro 如何批量删除字段
  • ​Python 3 新特性:类型注解
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • #《AI中文版》V3 第 1 章 概述
  • #pragma预处理命令
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • (1)(1.13) SiK无线电高级配置(六)
  • (12)目标检测_SSD基于pytorch搭建代码
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (附源码)计算机毕业设计高校学生选课系统
  • (六)vue-router+UI组件库
  • (五)MySQL的备份及恢复
  • (一)【Jmeter】JDK及Jmeter的安装部署及简单配置
  • (一)插入排序
  • (转)http-server应用
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (转)负载均衡,回话保持,cookie
  • ******IT公司面试题汇总+优秀技术博客汇总
  • .bashrc在哪里,alias妙用
  • .CSS-hover 的解释
  • .NET Framework .NET Core与 .NET 的区别
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .net 使用ajax控件后如何调用前端脚本