手把手教你实现一个通用的安卓权限请求工具

背景:

众所周知,安卓6.0以后有了权限动态申请机制,很多功能需要在运行时申请权限,下面就来一起实现一个申请运行时权限的工具类吧

前提:

首先,动态申请权限一般的写法是在activity里调用“ActivityCompat.requestPermissions”,然后重写activity的“onRequestPermissionsResult”方法获取授权结果,但是,这样写会有两个问题,

  • 1.破坏activity的结构,在每个需要申请权限的activity的方法都需要重写“onRequestPermissionsResult”方法,不太好,尤其是在成熟的大项目中。
  • 2.当申请权限的地方不是activity时,会比较麻烦,有人问,不在activity中在哪呢,这就要看你们的业务需求了,反正我还真遇到了这中情况,显然就无法通过重写“onRequestPermissionsResult”来获取授权回调了。
怎么做:
  1. 通过分析以上两点,接下来要做的已经很清晰了,我们可以通过打开一个透明的activity来实现权限申请,activity中除了动态申请权限外什么都不做,在此透明activity的“onRequestPermissionsResult”方法里,通过一个接口将授权结果回调回去
  2. 乍一看好像可行,实际上在大多数情况下确实是可行的,但是我在项目开发中又遇到一个问题,那就是,申请权限的那个activity不在主进程的时候,无法获取到权限授权结果,也就是需要权限的地方和透明申请权限的activity不在一个进程,导致无法回调授权结果,这时又有人可能会问,为毛会有activity不在主进程呢?嗯,项目大了什么代码都可能有,反正就不在主进程,这该怎么办呢?其实很好办,用进程间通信。好了,知道怎么做了,现在开始吧。
所用到的知识
  • 1.权限的动态申请
  • 2.Handler
  • 3.进程间通信

开始:
RequestPermissionUtil

1.第一步首先应该是个工具类了,名字就叫RequestPermissionUtil

public class RequestPermissionUtil {
    private static volatile RequestPermissionUtil instance;
    private Messenger mMessenger;
    private Context mContext;
    private OnPermissionListener mListener;
    private String[] mPermissionList;

    public static RequestPermissionUtil getInstance() {
        if (instance == null) {
            synchronized (RequestPermissionUtil.class) {
                if (instance == null) {
                    instance = new RequestPermissionUtil();
                }
            }
        }
        return instance;
    }

    private RequestPermissionUtil() {
    }
}

2.当然这个类目前什么都没做,接下来给它添加一些必要的方法

public class RequestPermissionUtil {
    private static volatile RequestPermissionUtil instance;
    private Messenger mMessenger;
    private Context mContext;
    private OnPermissionListener mListener;
    private String[] mPermissionList;

    public static RequestPermissionUtil getInstance() {
        if (instance == null) {
            synchronized (RequestPermissionUtil.class) {
                if (instance == null) {
                    instance = new RequestPermissionUtil();
                }
            }
        }
        return instance;
    }

    private RequestPermissionUtil() {
    }


    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (mListener != null && msg.getData().get(RequestPermissionService.RESP_RESULT) != null) {
                mListener.onPermissionResult((Boolean) msg.getData().get(RequestPermissionService.RESP_RESULT));
            }
            mContext.unbindService(mServiceConnection);
            return true;
        }
    });

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMessenger = new Messenger(service);
            Message mMessage = Message.obtain(null, RequestPermissionService.MSG_FROMCLIENT);
            mMessage.replyTo = new Messenger(mHandler);
            mMessage.obj = mPermissionList;
            try {
                mMessenger.send(mMessage);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    public void requestPermissions(Context context, String[] permissionArr, OnPermissionListener listener) {
        if (context == null || listener == null) {
            return;
        }
        mContext = context;
        mListener = listener;
        mPermissionList = permissionArr;
        if (Build.VERSION.SDK_INT <= 22 || allOpen(permissionArr)) {
            mListener.onPermissionResult(true);
        } else {
            Intent intent = new Intent(mContext, RequestPermissionService.class);
            mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        }
    }

    private boolean allOpen(String[] permissionArr) {
        if (permissionArr == null || permissionArr.length == 0) {
            return true;
        }
        for (String str : permissionArr) {
            if (ContextCompat.checkSelfPermission(mContext, str) !=
                    PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    public interface OnPermissionListener extends Serializable {

        void onPermissionResult(boolean success);

    }
}

一下加了这么多代码,下面就开始慢慢介绍这都是来干嘛的,就从下向上看

  • 首先,看到的是一个OnPermissionListener接口,很明显这是来处理授权结果回调的
  • 再向上看到一个allOpen函数,这个是检测权限是否开启,开启的话直接回调,就不用去申请了
  • 接下来看到一个比较重要的方法“requestPermissions”,第一个参数传context,这个context主要用来开启service,第二个参数传权限列表,第三个参数传回调函数,进入函数内部,先检查当前安卓版本小于6.0或者权限已经全部开启,直接回调开启,否则的话,去绑定一个服务,其中绑定函数中“mServiceConnection”接下来会介绍
  • 再向上看是一个ServiceConnection,这个类的实例mServiceConnection在bindService的时候使用,当服务连接时会调用ServiceConnection的onServiceConnected方法,我们可以在这个方法给service发送一些消息,这里我们new了一个Messenger,构造方法里传入了Binder类型的service,这样就可以通过这个Messenger给service发消息了,然后我们又获取了一个message对象,给这个的message对象的replayTo属性赋值了一个构造方法里传入了一个Handler的Messager对象,这一步是干嘛的呢?刚刚说过我们经过一些操作已经可以给service发消息了,可是service怎么向我们这里发消息呢?刚刚那个方法就是用来解决这个问题的,Service发给我们的消息都会在构造方法里的那个mHandler的回调里执行。然后我们把权限列表赋值给了msg的obj,最后通过mMessenger将消息发送给服务端,这样服务端就能接收到这个msg了。
  • 再向上看,是一个Handler,刚刚说过这个handler是用来处理service发来的消息的,同时在该方法里解除对service的绑定,因为通信理论上只需要一次
RequestPermissionService

接下来介绍这个中介:RequestPermissionService,看代码

public class RequestPermissionService extends Service {
    public static final int MSG_FROMCLIENT = 1000;
    public static final String RESP_RESULT = "result";
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(final Message msg) {
            switch (msg.what) {
                case MSG_FROMCLIENT:
                    final Messenger mMessenger = msg.replyTo;
                    String[] permissionList = (String[]) msg.obj;
                    RequestPermissionActivity.start(getApplicationContext(), permissionList, new RequestPermissionUtil.OnPermissionListener() {

                        @Override
                        public void onPermissionResult(boolean success) {
                            Message mMessage = Message.obtain(null, RequestPermissionService.MSG_FROMCLIENT);
                            Bundle mBundle = new Bundle();
                            mBundle.putBoolean(RESP_RESULT, success);
                            mMessage.setData(mBundle);
                            try {
                                mMessenger.send(mMessage);
                            } catch (RemoteException e) {
                                e.printStackTrace();
                            }
                        }
                    });

                    break;
            }
            return true;
        }
    });

    @Override
    public IBinder onBind(Intent intent) {
        return new Messenger(mHandler).getBinder();
    }
}

这里代码就比较少了,还是从下向上介绍

  • 首先看到一个onBind方法,经常背面试题的同学都知道,这个方法会在binderService的时候被调用,返回的那个IBinder就是之前说的那个在ServiceConnection的onServiceConnected的service,这里构造方法里也传了一个Handler,这个handler同时用来接受消息和发送消息
  • 再向上看那个handler,我们接受到消息后,得到要请求的权限数组,就开启权限请求了,在这里我们直接看onPermissionResult方法,获取回调中的权限申请结果,将msg发送给客户端那边
RequestPermissionActivity

接下来看这个RequestPermissionActivity,有几处细节还是值得说一下的

public class RequestPermissionActivity extends AppCompatActivity {

    private static final int REQUEST_CODE = 1;
    private static RequestPermissionUtil.OnPermissionListener transferOnPermissionListener;
    private RequestPermissionUtil.OnPermissionListener mOnPermissionListener;
    private boolean mResult = false;
    public static final String EXTRA_PERMISSIONS = "permissions";
    private String[] mPermissionList = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (Build.VERSION.SDK_INT >= 19) {
            Window window = getWindow();
            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
        getIntentData();

        ActivityCompat.requestPermissions(RequestPermissionActivity.this, mPermissionList, REQUEST_CODE);

    }

    private void getIntentData() {
        mOnPermissionListener = transferOnPermissionListener;
        transferOnPermissionListener = null;
        mPermissionList = getIntent().getStringArrayExtra(EXTRA_PERMISSIONS);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (grantResults != null && grantResults.length > 0) {
            mResult = true;
            for (int tmp : grantResults) {
                if (tmp != PackageManager.PERMISSION_GRANTED) {
                    mResult = false;
                    break;
                }
            }
        }
        finish();
    }

    @Override
    public void finish() {
        super.finish();
        overridePendingTransition(0, 0);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mOnPermissionListener != null) {
            mOnPermissionListener.onPermissionResult(mResult);
        }
    }

    public static void start(Context context, String[] permissionList, RequestPermissionUtil.OnPermissionListener listener) {
        Intent intent = new Intent(context, RequestPermissionActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra(RequestPermissionActivity.EXTRA_PERMISSIONS, permissionList);
        context.startActivity(intent);
        transferOnPermissionListener = listener;
    }
}

继续从下向上看

  • 这是刚刚在service里调用的start方法,第一个参数传context用于打开Activity,第二个参数传权限数组,第三个参数传回调接口,其中加intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)保证了context不是activity的时候也能正常打开这个activity
  • 再向上看destroy方法,在这里处理回调结果,保证了回调只执行一次
  • finish方法去掉了动画
  • onRequestPermissionsResult获取授权结果,并直接finish界面保证授权结果回调回去

总结

总的来说代码还是比较简单的,我将上面的这些代码封装成了一个lib,点击这里(github)可以下载查看示例

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值