转载本文需注明出处:微信公众号EAWorld,违者必究。
前言:
Android 是一个权限分隔的操作系统,其中每个应用都有其独特的系统标识。在默认情况下任何应用都没有权限执行对其他应用、操作系统或用户有不利影响的任何操作。这包括读取或写入用户的私有数据(例如联系人或电子邮件)、读取或写入其他应用程序的文件、执行网络访问、使设备保持唤醒状态等。
----引用自谷歌Android开发文档
目录:
1、Android权限的演化
2、运行时权限的申请
3、Android权限开源库
4、如何优雅地申请权限
1.Android权限的演化
Android6.0之前
Android6.0之前,应用权限仅需在代码里AndroidManifest.xml中声明便可以获得,不需要征求用户的同意。有的App一股脑申请了大量的权限,甚至一些工具类应用居然申请短信、录音、读取手机文件等敏感权限。当然,那也是流氓软件最盛行的年代,无数应用在后台偷鸡摸狗,盗取用户敏感数据。
Android6.0之后
Android6.0之后,应用权限被谷歌分成了两类,正常权限和危险权限。正常权限在AndroidManifest.xml中声明即可获得,危险权限则需要在使用前向用户申请,征得用户的同意后才可以使用。若没有向用户申请就执行操作,应用直接报错闪退。
危险权限和权限组:
2.运行时权限的申请
使用Android权限的原则
根据谷歌官方文档的说明,建议遵守以下四点原则:
仅使用应用正常工作所需的权限
注意库所需的权限
公开透明
让系统以显式方式访问
简单来说,除非真的需要,否则不要请求获取权限。
如何申请权限
判断是否已获取权限
int hasPermission=ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasPermission == PackageManager.PERMISSION_GRANTED) {
//已获取权限
}else{
//未获取权限
}
(左右滑动查看全部代码)
申请权限
ActivityCompat.requestPermissions(this, new String[]
{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MainActivity.RequestPermissionCode);
(左右滑动查看全部代码)
在Activity中注册回调
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == RequestPermissionCode){
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
//用户同意了权限申请
}else{
//用户拒绝了权限申请,建议向用户解释权限用途
}
}
}
(左右滑动查看全部代码)
3.Android权限开源库
通过上述示例看到申请权限代码比较繁琐,需要判断权限、申请权限、在Activity中注册权限申请结果的回调。社区中有很多运行时权限的开源库,下面github上star比较多的这四个。
PermissionsDispatcher
本库基于注解来实现,且支持Java/Kotlin。因为是在你实现的方法上加注解来请求权限,所以代码相对要简洁一些,我们基本上要使用到以下几个注解。
同样,在写完申请完权限后执行的方法后,同样要在Activity的onRequestPermissionsResult中注册回调。
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// NOTE: delegate the permission handling to generated function
onRequestPermissionsResult(requestCode, grantResults)
}
(左右滑动查看全部代码)
RxPermissions
同样也是一个优秀的开源库,这个库提供了如同RxJava风格的权限申请方法,代码简洁,只需要AppCompatActivity即可初始化,并可以在任意位置调用。但需要引入RxJava库。
final RxPermissions rxPermissions = new RxPermissions(this);
// Must be done during an initialization phase like onCreate
rxPermissions
.request(Manifest.permission.CAMERA)
.subscribe(granted -> {
if (granted) { // Always true pre-M
// I can control the camera now
} else {
// Oups permission denied
}
});
(左右滑动查看全部代码)
easypermissions
googlesamples中提供的方法,使用EasyPermissions.requestPermissions申请权限,同时也需要在Activity的onRequestPermissionsResult中注册回调。
AndPermission
仅支持androidx,同样需要Activity来初始化,代码也比较简洁。
AndPermission.with(this)
.runtime()
.permission(Permission.Group.STORAGE)
.onGranted(permissions -> {
// Storage permission are allowed.
})
.onDenied(permissions -> {
// Storage permission are not allowed.
})
.start();
(左右滑动查看全部代码)
总的来说,每个库都有各自的优缺点,大家可以根据具体需求选用最适合自己项目的库。
4.如何优雅地申请权限
吐槽:开源库代码繁琐,文档有限,问题解答不及时。。。
各自项目有着不同的需求,这些丰富的开源库可能仍然无法满足我们的要求,不仅是权限申请,其他功能也是一样。接下来将手把手带大家造一个简化权限申请代码的轮子。
整体思路
绝大多数开源库在申请权限的时候要在Activity中onRequestPermissionsResult注册回调,这一点我是很反感的,代码侵入性太大了。
假如我封装了一个获取定位的接口,这是一个独立的方法,一般来说会写在LocationUtils.java中,而且任何人任何类类都可能调用我的方法,这就导致LocationUtils是没有Activity去接收onRequestPermissionsResult回调的数据。相信这也是大多数开发者遇到的主要问题之一。
所以,在应用中,我可以加载一个Fragment(和RxPermissions思路类似),在fragment中申请权限,onRequestPermissionsResult回调也放在这个fragment中。这样我在任何位置,只要有Activity存在,都可以加载这个fragment去请求权限,请求完成后再移除这个fragment。
public static void requestPermission(final Activity context, final String[] permissions, PermissionCallback permissionCallback) {
permissionFragment.setOnAttachCallback(new FragmentAttachCallback() {
@Override
public void onAttach() {
permissionFragment.requestPermission(permissions);
}
});
permissionFragment.setOnPermissionCallback(permissionCallback);
FragmentTransaction fragmentTransaction = context.getFragmentManager().beginTransaction();
//让我在评论区看到你们的777
fragmentTransaction.add(permissionFragment, "permissionFragment@777").commit();
(左右滑动查看全部代码)
当然我们也可以借助getTopActivity方法,让权限库自己去获取栈顶的Activity,这样只需要传入需要申请的权限和权限结果的回调即可。
PermissionAnywhere.requestPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}
, permissionCallback);
(左右滑动查看全部代码)
其中PermissionCallback中会回调用户点击同意的权限,用户点击拒绝的权限,用户点击不再提示且拒绝的权限三种。
public interface PermissionCallback {
void onComplete(List grantedPermissions, List deniedPermissions, List alwaysDeniedPermissions);
}
(左右滑动查看全部代码)
使用方法
1、在根目录build.gradle中增加
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
(左右滑动查看全部代码)
2、增加依赖
dependencies {
implementation 'com.github.mingyuers:PermissionAnywhere:latest.release'
}
(左右滑动查看全部代码)
代码下载地址
以上完整代码已开源到github:
https://github.com/mingyuers/PermissionAnywhere
欢迎大家研究学习,欢迎star,欢迎pr。
延伸
其实也可以使用1px的Activity进行权限申请,这样能否实现在Application中申请权限?会不会引申出别的问题呢?欢迎大家在留言区讨论。
推荐阅读
浅谈安卓apk加固原理和实现
React-native如何变为移动端的弄潮儿
低代码平台在移动开发方面的缺陷
关于作者:明月,现任普元移动团队资深开发工程师,长期致力于IT技术研究,产品设计和开发等工作,擅长Java、NodeJs、ReactNative等领域技术。先后参加深圳登、太保等移动项目的实施,参与Mobile 8.0移动平台的设计开发工作。
关于EAWorld:微服务,DevOps,数据治理,移动架构原创技术分享。长按二维码关注!
课程预告! 4月13日(周五)下午14:30普元移动开发工程师大扑棱鱼为大家分享《从Mobile8.0 平台与微应用剖析RN组件生命周期》,在此公众号回复“YG+微信号”马上入群并完成报名!
在看点这里