Android运行时权限

1.在Android6.0之前只需在AndroidManifest.xml文件写明权限即可。但是在Android6.0之后也就是SDK>=23的时候,一些隐私权限需要动态申请,而且在用户同意授权之后App才能拥有该权限。

如下9组权限需要动态申请。而且一组权限只要一个授权授权同意,全组都可用。

  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

2.动态权限总共有三个方法:

ContextCompat.checkSelfPermission() 检查权限是否已经授权

ActivityCompat.checkSelfPermission() 检查权限是否已经授权

ActivityCompat.requestPermissions()动态申请权限,并弹出对话框。

onRequestPermissionsResult()在activity里面重写该方法该方法是权限申请之后的回调方法。

3.activity中的申请:

package com.ysl.mymaterialdesign.activity;

import android.Manifest.permission;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

import java.util.Arrays;

public class PermissionActivity extends AppCompatActivity {
    private static final int PERMISSION_CAMERA_CODE = 0;
    private static final String PERMISSION_CAMERA = permission.CAMERA;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DealPermission(PERMISSION_CAMERA, PERMISSION_CAMERA_CODE);
    }

    private void DealPermission(String permission, int requestCode) {
        int checkSelfPermission = ActivityCompat.checkSelfPermission(PermissionActivity.this, permission);
//        int checkSelfPermission1 = ContextCompat.checkSelfPermission(PermissionActivity.this, permission.CAMERA);
        if (checkSelfPermission == PackageManager.PERMISSION_GRANTED){
            //权限已被授权
            System.out.println("权限已被授权, 开始做事");
        }else {
            //权限未被授权,重新申请
            System.out.println("权限未被授权,重新申请");
            ActivityCompat.requestPermissions(PermissionActivity.this, new String[]{permission}, requestCode);
        }
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        System.out.println("requestCode = "+requestCode+"  permissions = "+Arrays.toString(permissions)+"  grantResults = "+Arrays.toString(grantResults));
        if (requestCode == PERMISSION_CAMERA_CODE) {//申请时的返回码一一对应
            //grantResults数组与权限字符串数组对应,里面存放权限申请结果
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
                System.out.println("权限已被授权, 开始做事");
            }else{
                System.out.println("拒绝授权");
                //如果是拒绝授权,可以弹出重要提示,或强制关闭页面。
            }
        }
    }
}

代码中注释也写的很清楚,先检测权限有没有授权。若已经授权,然后再去做自己的业务。

4.上面的是申请了一个,假如申请一组:

写一个分组的类:

package com.ysl.mymaterialdesign.activity;

import android.Manifest;
import android.os.Build;

public class MyPermission {
    public static final String[] CALENDAR;
    public static final String[] CAMERA;
    public static final String[] CONTACTS;
    public static final String[] LOCATION;
    public static final String[] MICROPHONE;
    public static final String[] PHONE;
    public static final String[] SENSORS;
    public static final String[] SMS;
    public static final String[] STORAGE;

    static {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            CALENDAR = new String[]{};
            CAMERA = new String[]{};
            CONTACTS = new String[]{};
            LOCATION = new String[]{};
            MICROPHONE = new String[]{};
            PHONE = new String[]{};
            SENSORS = new String[]{};
            SMS = new String[]{};
            STORAGE = new String[]{};
        } else {
            CALENDAR = new String[]{
                    Manifest.permission.READ_CALENDAR,
                    Manifest.permission.WRITE_CALENDAR};

            CAMERA = new String[]{
                    Manifest.permission.CAMERA};

            CONTACTS = new String[]{
                    Manifest.permission.READ_CONTACTS,
                    Manifest.permission.WRITE_CONTACTS,
                    Manifest.permission.GET_ACCOUNTS};

            LOCATION = new String[]{
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION};

            MICROPHONE = new String[]{
                    Manifest.permission.RECORD_AUDIO};

            PHONE = new String[]{
                    Manifest.permission.READ_PHONE_STATE,
                    Manifest.permission.CALL_PHONE,
                    Manifest.permission.READ_CALL_LOG,
                    Manifest.permission.WRITE_CALL_LOG,
                    Manifest.permission.USE_SIP,
                    Manifest.permission.PROCESS_OUTGOING_CALLS};

            SENSORS = new String[]{
                    Manifest.permission.BODY_SENSORS};

            SMS = new String[]{
                    Manifest.permission.SEND_SMS,
                    Manifest.permission.RECEIVE_SMS,
                    Manifest.permission.READ_SMS,
                    Manifest.permission.RECEIVE_WAP_PUSH,
                    Manifest.permission.RECEIVE_MMS};

            STORAGE = new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE};
        }
    }

}
package com.ysl.mymaterialdesign.activity;

import android.Manifest.permission;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

import java.util.Arrays;

public class PermissionActivity extends AppCompatActivity {
    private static final int PERMISSION_GROUP_CODE = 1;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DealPermissionGroup(MyPermission.STORAGE, PERMISSION_GROUP_CODE);
    }

    private void DealPermissionGroup(String[] permissionGroup, int permissionGroupCode) {
        int checkSelfPermission = ActivityCompat.checkSelfPermission(PermissionActivity.this, permissionGroup[0]);
        if (checkSelfPermission == PackageManager.PERMISSION_GRANTED){
            //权限已被授权
            System.out.println("权限已被授权, 开始做事"+ permissionGroup[0]);
        }else {
            //权限未被授权,重新申请
            System.out.println("权限未被授权,重新申请");
            ActivityCompat.requestPermissions(PermissionActivity.this, permissionGroup, permissionGroupCode);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        System.out.println("requestCode = "+requestCode+"  permissions = "+Arrays.toString(permissions)+"  grantResults = "+Arrays.toString(grantResults));
         if (requestCode == PERMISSION_GROUP_CODE){
            for(int i=0; i<grantResults.length; i++){
                if(grantResults[i]==PackageManager.PERMISSION_GRANTED){
                    System.out.println("已被授权");
                } else {
                    System.out.println("拒绝授权");
                    //如果是拒绝授权,可以弹出重要提示,或强制关闭页面。
                }
            }
        }
    }
}

只需要申请每组中的任意一个,如果被授权了,其他的也会自动授权。

如果申请多组权限,调用多次DealPermissionGroup(String[] permissionGroup, int permissionGroupCode)即可。

5.注意点:

假如我现在动态申请READ_PHONE_STATE权限;

它属于的权限组:

PHONE = new String[]{
                    Manifest.permission.READ_PHONE_STATE,
                    Manifest.permission.CALL_PHONE,
                    Manifest.permission.READ_CALL_LOG,
                    Manifest.permission.WRITE_CALL_LOG,
                    Manifest.permission.USE_SIP,
                    Manifest.permission.PROCESS_OUTGOING_CALLS};

调用:

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DealPermissionGroup(MyPermission.PHONE, PERMISSION_LOCATION_CODE);
    }

错误一:没有任何权限申请的提示框:因为清单文件中没有写。

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

错误二:PHONE下面有六个权限:

下面看申请的结果:

看日志,只有第一个授权了,其他的5个都没有授权。

下面我们把其他的5个也添加到清单文件中:

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <uses-permission android:name="android.permission.READ_CALL_LOG"/>
    <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
    <uses-permission android:name="android.permission.USE_SIP"/>
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>

结果:

也就是说,在一组权限中,我们申请的时候,系统只会弹出来一个框来供用户授权。如果授权成功了。假如再申请这组的其他权限(在清单文件中申请),系统默认都会授权。但如果没有在清单文件中写这些权限。系统也是不会授权的。此时,假如我们直接使用这组中的其他权限,可能会出错。所以,在程序中,使用的权限,一定要在清单文件中写出来。

6.假如我一次申请了4组权限:

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DealPermission(PERMISSION_CAMERA, PERMISSION_CAMERA_CODE);
        DealPermissionGroup(MyPermission.STORAGE, PERMISSION_STORAGE_CODE);
        DealPermissionGroup(MyPermission.LOCATION, PERMISSION_LOCATION_CODE);
        DealPermissionGroup(MyPermission.PHONE, PERMISSION_LOCATION_CODE);
    }

测试发现:Android6.0上,程序以启动会弹出四个授权框,让我点击四次来授权。但是7.0,8.0,9.0都只会弹出第一个授权相机的框,其他的都没有弹出来。只有退出app,再次进来,才会弹出第二个提示框。也就是说,我要退出四次,才能把需要的权限申请完成。这显然是不合理的,假如第一次就要使用后面的权限,会导致app报错的。

7.基于上面的错误,我们可以使用第三方库 

PermissionDispatcher : Androidstudio还有插件,直接点击两下就完成了。

我的:

//依赖
implementation('com.github.hotchemi:permissionsdispatcher:3.1.0')
annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:3.1.0'

//代码
package com.ysl.mymaterialdesign.activity;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

import permissions.dispatcher.NeedsPermission;
import permissions.dispatcher.OnNeverAskAgain;
import permissions.dispatcher.OnPermissionDenied;
import permissions.dispatcher.OnShowRationale;
import permissions.dispatcher.PermissionRequest;
import permissions.dispatcher.RuntimePermissions;

@RuntimePermissions
public class PermissionDispatcherActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        PermissionDispatcherActivityPermissionsDispatcher.needsWithPermissionCheck(this);
    }

    @NeedsPermission({Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
    void needs() {
        Toast.makeText(this, "getNeeds", Toast.LENGTH_SHORT).show();
    }

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

    @OnShowRationale({Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
    void show(final PermissionRequest request) {
        new AlertDialog.Builder(this)
                .setMessage("此APP需要以下权限,下一步将请求权限")
                .setPositiveButton("下一步", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        request.proceed();//继续执行请求
                    }
                }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                request.cancel();//取消执行请求
            }
        })
                .show();

    }

    @OnPermissionDenied({Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
    void denied() {
        Toast.makeText(this, "已拒绝一个或以上权限", Toast.LENGTH_SHORT).show();
    }

    @OnNeverAskAgain({Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
    void neverAskAgain() {
        Toast.makeText(this, "已拒绝一个或以上权限,并不再询问", Toast.LENGTH_SHORT).show();
    }
}

优点:有插件,集成简单;申请多个时,非常方便

缺点:没有权限检测的方法,当同时申请多个权限时,当有多个拒绝的,或多个拒绝并不再询问的,只会提示出来,而不能指出到底哪个被拒绝了。

详细使用请看:

https://blog.csdn.net/s13383754499/article/details/79034758

EasyPermissions: Google官方封装的库

我的:

//依赖
implementation 'pub.devrel:easypermissions:2.0.0'

//代码
package com.ysl.mymaterialdesign.activity;

import android.Manifest;
import android.Manifest.permission;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;

import java.util.List;

import pub.devrel.easypermissions.AppSettingsDialog;
import pub.devrel.easypermissions.EasyPermissions;


public class EasyPermissionsActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks, EasyPermissions.RationaleCallbacks{
    private static final int CAMERA_REQUESTCODE = 101;
    private static final int LOCATION_CONTACTS_REQUESTCODE = 102;
    private static final String TAG = "EasyPermissionsActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestCamera();
        requestLocationAndContacts();
        easyCheckPermissions(this, new String[]{permission.CAMERA});
    }

    public void requestCamera() {
        if (EasyPermissions.hasPermissions(this, Manifest.permission.CAMERA)) {
            Toast.makeText(this, "已授权 Camera", Toast.LENGTH_LONG).show();
        } else {
            // request for one permission
            EasyPermissions.requestPermissions(this, "需要相机权限",
                    CAMERA_REQUESTCODE, Manifest.permission.CAMERA);
        }
    }

    public void requestLocationAndContacts() {
        String[] perms = { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_CONTACTS };
        if (EasyPermissions.hasPermissions(this, perms)) {
            Toast.makeText(this, "已授权 联系人和 位置信息", Toast.LENGTH_LONG).show();
        } else {
            // request for both permissions
            EasyPermissions.requestPermissions(this, "需要位置和联系人权限",
                    LOCATION_CONTACTS_REQUESTCODE, perms);
        }
    }

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

    @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
        Log.d(TAG, "onPermissionsGranted ---> requestCode = "+requestCode+" perms = "+perms);
        switch (requestCode){
            case CAMERA_REQUESTCODE:
                Toast.makeText(this, "已获取权限"+perms, Toast.LENGTH_SHORT).show();
                break;
            case LOCATION_CONTACTS_REQUESTCODE:
                Toast.makeText(this, "已获取权限"+perms, Toast.LENGTH_SHORT).show();
                Log.d(TAG, "已获取权限"+perms);
                break;
        }
    }

    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
        Log.d(TAG, "onPermissionsDenied ---> requestCode = "+requestCode+" perms = "+perms);

        switch (requestCode) {
            case CAMERA_REQUESTCODE:
                Toast.makeText(this, "已拒绝权限" + perms, Toast.LENGTH_SHORT).show();
                break;
            case LOCATION_CONTACTS_REQUESTCODE:
                Toast.makeText(this, "已拒绝权限"+ perms, Toast.LENGTH_SHORT).show();
                Log.d(TAG, "已拒绝权限"+ perms);
                break;
        }
        //假如设置了不再询问,就弹出去设置页面修改的选项
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            Toast.makeText(this, "已拒绝权限" + perms + "并不再询问" , Toast.LENGTH_SHORT).show();
            Log.d(TAG, "已拒绝权限" + perms + "并不再询问");
            new AppSettingsDialog
                    .Builder(this)
                    .setRationale("此功能需要" + perms + "权限,否则无法正常使用,是否打开设置")
                    .setPositiveButton("好")
                    .setNegativeButton("不行")
                    .build()
                    .show();
        }
    }

    @Override
    public void onRationaleAccepted(int requestCode) {

    }

    @Override
    public void onRationaleDenied(int requestCode) {

    }

    //使用EasyPermissions查看权限是否已申请
    //可以传一个或多个权限字符串,也可以把这些放在字符串数组中来传参
    //传入多个的时候,只要有一个没有授权,就会返回false;全部都授权了,才会返回true。
    public boolean easyCheckPermissions(Context context, String ... permissions) {
        boolean hasPermissions = EasyPermissions.hasPermissions(context, permissions);
        return hasPermissions;
    }
}

优点:可以申请一个或多个权限;可以检测一个或多个权限;申请多个权限时,多个权限的申请结果很清晰,谁同意了,谁被拒绝了,谁被拒绝并且不再询问了。而且当不再询问时,还有提示框可以跳转到设置页面(对于私下把app权限禁止了,在app中使用此功能时,做提示很有用)。

缺点:写起来代码稍多。

详细使用请看:

https://blog.csdn.net/zhu522959034/article/details/73647860

RxPermissions:可以配合rxJava或rxJava2一起使用

我的:

//依赖
implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
//  implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.3@aar'
implementation 'com.github.tbruyelle:rxpermissions:0.10.2'

//代码
package com.ysl.mymaterialdesign.activity;

import android.Manifest;
import android.Manifest.permission;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.tbruyelle.rxpermissions2.Permission;
import com.tbruyelle.rxpermissions2.RxPermissions;

import io.reactivex.functions.Consumer;

public class RxPermissionActivity extends AppCompatActivity {
    private static final String TAG = "RxPermissionActivity";
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestPermissions();
        checkPermissionsIsGranted(this);
    }

    private void requestPermissions() {
        RxPermissions rxPermission = new RxPermissions(RxPermissionActivity.this);
        rxPermission
                .requestEach(Manifest.permission.ACCESS_FINE_LOCATION,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.CAMERA)
                .subscribe(new Consumer<Permission>() {
                    @Override
                    public void accept(Permission permission) throws Exception {
                        if (permission.granted) {
                            // 用户已经同意该权限
                            Log.d(TAG, permission.name + " is granted.");
                        } else if (permission.shouldShowRequestPermissionRationale) {
                            // 用户拒绝了该权限,没有选中『不再询问』(Never ask again),那么下次再次启动时。还会提示请求权限的对话框
                            Log.d(TAG, permission.name + " is denied. More info should be provided.");
                        } else {
                            // 用户拒绝了该权限,而且选中『不再询问』
                            Log.d(TAG, permission.name + " is denied.并且选中了不再询问。");
                        }
                    }
                });
    }

    //检查某个权限是否被申请,只支持单个权限的检测
    public boolean checkPermissionsIsGranted(FragmentActivity activity) {
        RxPermissions permissions = new RxPermissions(activity);
        permissions.setLogging(true);

        boolean granted = permissions.isGranted(permission.WRITE_EXTERNAL_STORAGE);
        Log.d(TAG, "checkPermissionsIsGranted ---> " + granted);
        return granted;
    }
}

优点:代码简洁,少;可以和rxJava配合使用;有单个权限检测功能;申请多个权限时,多个权限的申请结果很清晰,谁同意了,谁被拒绝了,谁被拒绝并且不再询问了。

缺点:依赖中要引入rxJava,rxAndroid包,rxJava版本不同对应的rxPermission版本也不一致。

详细使用请看:

https://www.jianshu.com/p/c1219d1d2401

以上只是个人感觉,三者详细比较可以看:

目前最流行的运行时权限请求框架PermissionsDispatcher、RxPermissions和easypermissions的使用和对比

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值