Android动态权限框架:PermissionsDispatcher
- 1. 敏感权限有如下
- 2. 流行运行时权限请求框架
- 3. 常见问题
- 4. 参考
自Android6.0开始,基于保护用户隐私角度进行设计的敏感权限,需要动态申请
1. 敏感权限有如下
1.1
相机权限 相机
android.permission.CAMERA
1.2
录音权限(麦克风) 麦克风
android.permission.RECORD_AUDIO
1.3
读写通讯录 联系人
// 读通讯录
android.permission.READ_CONTACTS
// 写通讯录
android.permission.WRITE_CONTACTS
1.4
读写SD卡权限 存储权限
// 读SD卡
android.permission.READ_EXTERNAL_STORAGE
// 写SD卡
android.permission.WRITE_EXTERNAL_STORAGE
1.5
定位权限 定位
android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_COARSE_LOCATION
1.6
访问手机状态(获取deviceId标记唯一设备) 手机状态
android.permission.READ_PHONE_STATE
SIM卡存储的数据可分为四类
- 第一类是固定存放的数据。这类数据在移动电话机被出售之前由SIM卡中心写入,包括国际移动用户识别号(IMSI)、鉴权密钥(KI)、鉴权和加密算法等等
- 第二类是暂时存放的有关网络的数据。如位置区域识别码(LAI)、移动用户暂时识别码(TMSI)、禁止接入的公共电话网代码等
- 第三类是相关的业务代码,如个人识别码(PIN)、解锁码(PUK)、计费费率等
- 第四类是电话号码簿,是手机用户随时输入的电话号码。用户全部资料几乎都存储在SIM卡内,因此SIM卡又称为用户资料识别卡
SIM卡的识别通常使用IMSI号
,这个对于SIM卡是唯一的
IMEI:
- 与 设备唯一对应,IMEI不存在于SIM卡中,它是手机本身的串号
1.7
日历
1.8
传感器
1.9
短信
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
2. 流行运行时权限请求框架
以上列举的是一般APP常用的几组敏感权限,作为Android开发者,目前最流行的运行时权限请求框架
- PermissionsDispatcher
- RxPermissions
- Easypermissions
- AndPermission
个人觉得PermissionsDispatcher
好用,耦合性也低,使用起来也没那么多if else逻辑判断,清晰明了,接下来贴一些代码描述一下它的使用步骤2.1
首先上一段的代码
/**
* 1.获取了写内部存储权限,这个是用户点击同意后调用方法
*/
@NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
public void writeStoragePermission() {
Toast.makeText(this, " 获取了写内部存储权限", Toast.LENGTH_SHORT).show();
}
// 2.向用户解释为什么需要调用该权限
// 只有当第一次请求权限被用户拒绝,下次请求权限之前会调用
// 我在解释的清楚点:比如第一次你请求访问SD卡权限,用户点击了拒绝并且没有勾选"不再提示"
// 当用户再次请求该权限时,会首先调用该方法,方法内部一般会弹框跟用户说明为撒需要该权限
// 当用户有点懂了,点击了继续在去请求该权限
@OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)
void showSingleRationale(final PermissionRequest request) {
new AlertDialog.Builder(this)
.setMessage("使用此功能需要写内部存储权限,下一步将继续请求权限")
.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();
}
/**
* 3.一旦用户拒绝了该权限,则调用
*/
@OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE)
public void storageDenied() {
Toast.makeText(this, "拒绝写内部存储权限", Toast.LENGTH_SHORT).show();
}
// 4.用户选择的不再询问,并且拒绝了该权限
// 以后每次用户在点击申请该权限,会直接调用该方法,一般在这里软件可以引导用户设置开启该权限
@OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE)
public void storageNeverAsk() {
Toast.makeText(this, "拒绝写内部存储权限,并且设置不再询问!", Toast.LENGTH_SHORT).show();
}
上面这段代码,有点基础的Android开发都应该知道基本使用方法了
2.2
在实际开发中,我们可以使用PermissionsDispatcher针对Android Studio开发的插件
1.
只要在setting设置里的plugins界面里搜索PermissionsDispatcher就可以安装了,安装完重启一下就能使用2.
在所需的Activity或者Fragment的代码里面右键,选择Generate,然后就可以选择Generate Runtime Permissions…
3. 常见问题
3.1
报错如下:Generated onRequestPermissionsResult method not called
Generated onRequestPermissionsResult method not called
这个是框架问题
,我集成的版本号
com.github.hotchemi:permissionsdispatcher:3.0.1
改为:3.3.1重新ReBuild一下就可以了
3.2
编译项目报错:Error: Program type already present: android.support.v13.view.DragStartHelper$OnDragStartListener
Error: Program type already present: android.support.v13.view.DragStartHelper$OnDragStartListener
这个是依赖不同版本导致的,比如我这个报错,在
// 在app的build.gradle中配置一下排除重复依赖包即可
dependencies {
......
configurations {
all*.exclude group: 'com.android.support', module: 'support-v13'
}
}
3.3
APP若被监管判定可能有获取手机应用列表风险,类似如下方法代码可能会被检测
/**
* 41. 判断市场是否存在的方法(遍历应用包名,看是否有对应包名存在)
* <p>
* 1. x 涂鸦预览分享到 QQ好友 会检测
* <p>
* 2. 网页调用分享到 QQ好友 或 QQ空间 :js method :qqShare
* <p>
* 3. x Http弹框分享到QQ :http callback method :showShareDialog
* <p>
* 4. 支付宝检测 :js method :isAlipayInstall
* <p>
* 5. x GameShareAct 游戏分享
*
* @param context
* @param packageName
* @return
*/
public static boolean isInstalled(Context context, String packageName) {
try {
final PackageManager packageManager = context.getPackageManager();
// 获取所有已安装程序的包信息
List<PackageInfo> pkgInfo = packageManager.getInstalledPackages(0);
List<String> pName = new ArrayList<>();// 用于存储所有已安装程序的包名
if (pkgInfo != null) {
for (int i = 0; i < pkgInfo.size(); i++) {
if (pkgInfo.get(i) != null && !TextUtils.isEmpty(pkgInfo.get(i).packageName)) {
pName.add(pkgInfo.get(i).packageName.toLowerCase());
}
}
}
// 判断pName中是否有目标程序的包名,有TRUE,没有FALSE
return (!TextUtils.isEmpty(packageName)) && pName.contains(packageName.toLowerCase());
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 判断市场是否存在的方法
*
* @param context
* @param packageName
* @return
*/
public static boolean isAvailable(Context context, String packageName) {
final PackageManager packageManager = context.getPackageManager();
// 获取所有已安装程序的包信息
List<PackageInfo> pkgInfo = packageManager.getInstalledPackages(0);
List<String> pName = new ArrayList<>();// 用于存储所有已安装程序的包名
if (pkgInfo != null) {
for (int i = 0; i < pkgInfo.size(); i++) {
String pn = pkgInfo.get(i).packageName;
pName.add(pn);
}
}
// 判断pName中是否有目标程序的包名,有TRUE,没有FALSE
return pName.contains(packageName);
}
4. 参考
- Android 跳转权限设置界面的终极适配
- Android中的各种访问权限Permission含义
- PermissionsDispatcher、RxPermissions和easypermissions的使用和对比
- PermissionsDispatcher
- 初探Android6.0权限库PermissionsDispatcher
- PermissionsApplyDemo
- PermissionsDispatcher使用详解
- Android开发之——依赖冲突Program type already present
- Android’s Runtime Permission
- Android应用的权限配置和权限列表
- Android 获取双卡手机IMEI,IMSI,ICCID
Permission Group | Permissions | 备注 |
CONTACTS | android.permission.GET_ACCOUNTS | |
CONTACTS | android.permission.WRITE_CONTACTS | ---- |
CONTACTS | android.permission.READ_CONTACTS | ---- |