Android搭建简约动态权限申请库

对于app的现状,应用的权限申请必不可少,不同的功能都可能用的上,所以把权限模块抽出来最好不过,另外附上提交maven仓库的脚本,如果公司本身有maven的仓库的话,可以用过脚本提交到maven仓库,到时候用的时候依赖就可以了。去查看github的时候,其实已经有蛮多的优秀权限申请的库了,本着公司少用开源库的原则,只好自己写一套了。

权限分组

一般系统分三种权限,一种是正常权限(不需要申请),另外一种是危险权限(需要申请),特殊权限(需要申请),具体介绍如下链接:
https://developer.android.google.cn/guide/topics/security/permissions#normal-dangerous

然后危险权限和对应的权限组如下:
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

在同一个权限组中,只要请求其中一个就可以了。
根据官方的例子:

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}

上面是官方的例子,权限申请有几个状态,首先判断权限是否授权,即

if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

当未授权时执行代码,然后在未授权的情况下有三种状态:
1、第一次申请的情况
2、用户已经拒绝过权限申请(rationnal)
3、用户拒绝并且选择了不再提示按钮

所以我们抽出来的功能要把这个三种情况覆盖,当第一次申请的时候,我直接提示授权框继续申请权限,当用户拒绝后,我们应该提示一个合理性提示框,向用户解释需要申请权限即rationnal dialog。如果用户拒绝并且选择不再提示的时候,权限的申请只能跳转到设置页面,然后用户手动允许权限功能了。

抽出权限申请的功能类,要最大化的方便使用,所以我定义一个方法的调用类PermissionManager,该类只提供三个方法

public static void requestPermission(android.app.Fragment holder, RationalInterface rationalInterface, int requestCode, 
String... permissions) 
public static void requestPermission(Fragment holder, RationalInterface rationalInterface, int requestCode, String... permissions) 
public static void requestPermission(Activity holder, RationalInterface rationalInterface, int requestCode, String... permissions) 

对于rational的情况对外提供接口,因为对于一些项目是需要展示不一样的ui的。
然后调用结束之后,会在宿主activity/fragmeng中回调相应的方法,成功、失败、不再提示

 @PermissionSuccess
    public void success(int requestCoe, String[] permissions) {
        Log.i(TAG, "PermissionSuccess");
    }

    @PermissionFail
    public void fail(int requestCoe, String[] permissions) {
        Log.i(TAG, "PermissionFail");

    }

    @PermissionAlwayDenied
    public void gotoSetting(int requestCoe, String[] permissions) {

然后继续分解,在执行requestPermission过程中

 /**
     * activity权限请求
     * 1先检查有哪些权限没授权,如有未授权,再检查这个未授权是不是rational,
     * 2再核查是不是用户拒绝并且点击不再提示,只能通过onRequestPermissionsResult去核实
     * 3如果没有未授权的 success
     *
     * @param holder
     * @param requestCode
     * @param permissions
     */
    public static void requestPermission(Activity holder, RationalInterface rationalInterface, int requestCode, String... permissions) {
        RequestPermission requestPermission = new RequestPermission.Builder(holder, requestCode, permissions)
                .setRationalInterface(rationalInterface).builder();
        if (!hasPermission(holder, permissions)) {
            List rationalList = getNationalPermission(requestPermission);
            //触发rationnal回调
            if (rationalList.size() > 0) {
                requestPermission.getRationalInterface().callback(requestPermission);
            } else {
                //走正常requestPermssion,这里包含了2
                requestPermission.getPermissionHelper().requesetPermissions(requestCode, permissions);
            }
            return;
        }
        //权限通过
        resolverAnnotateSuccess(holder, requestCode, permissions);

    }

先是判断是否有权限,如果权限全部通过,则会走@PermissionSuccess的方法,如果有权限需要申请,先判断该权限是否已经被拒绝过,如果已经被拒绝过则走rationnal的回调过程,如果未拒绝或拒绝选择了不再提示,则走 requestPermission.getPermissionHelper().requesetPermissions(requestCode, permissions);

下一步是正确的权限申请方法,因为宿主可能是activity或者fragment,他们的处理情况不一样,所以要分情况进行处理,由此建立了基类:

public abstract class AbstractPermissionHelper<T> {
    /**
     * 是否rationnal
     * @param perm
     * @return
     */
    public abstract boolean shouldShowRequestPermissionRationale(String perm);

    /**
     * 权限请求
     * @param requestCode
     * @param perm
     */
    public abstract void requesetPermissions(int requestCode, String[] perm);

    /**
     * 是否选择不再提示
     * @param perms
     * @return
     */
    public abstract boolean somePermissionAlwayDenied( List<String> perms);


    public abstract T getHolder();

    public abstract Context getContext();

}

activity、fragment、v4.fragment分别具体实现方法
下一步,因为选择了不再提示的时候,需要通过宿主activit或者fragmeng的
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
回调方法才能判断,又因为不想在调用的时候麻烦,所以就单独建立了一个activity并且设置透明效果,单独从事权限申请的结果回调工作。代码如下:

public class PermissionActivity extends AppCompatActivity {
    private static final String KEY_PERMISSION = "KEY_PERMISSION";
    private static AbstractPermissionHelper permissionHelper;
    public static void activityRequestPermission(AbstractPermissionHelper permissionHelper, String[] permissions) {
        PermissionActivity.permissionHelper = permissionHelper;
        Intent intent = new Intent(permissionHelper.getContext(), PermissionActivity.class);
        intent.putExtra(KEY_PERMISSION, permissions);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        permissionHelper.getContext().startActivity(intent);
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] permissions = getIntent().getStringArrayExtra(KEY_PERMISSION);
        requestPermissions(permissions, 1);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        PermissionManager.onRequestPermissionsResult(requestCode, permissions, grantResults, permissionHelper);
        finish();
    }

到最后面,当用户选择了不再提示按钮时,我们需要跳转到设置页面进行手动授权,至此,框架封装结束了,在调用页调用一下方法即可:

PermissionManager.requestPermission(MainActivity.this, new RationalDialog(), 1, Manifest.permission.READ_EXTERNAL_STORAGE, 
Manifest.permission.READ_CONTACTS);

附上项目github,欢迎star和鼓励
https://github.com/liangzs/permissionDemo

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容