权限大全看这里:
权限科普文看这里:
自打 API 23(6.0)之后,android 权限必须要动态去获取啦,变化真的是好大,刚开始真是不适应
6.0 之后把权限分成2种:
- Normal : 普通权限
- Dangerous : 危险权限
Normal 的权限和以前使用的方式一样,在配置文件里声明系统就会给,但是Dangerous 的权限就不行了,google 把涉及到用户隐私的权限都划归到 Dangerous 里面了,我们仅仅在配置文件里声明的话,使用到这个权限的代码就会直接 carsh ,蛋疼的设计。
Dangerous 权限范围:
- calendar 时间(日历)
- camera 相机
- contacts 联系人(读,写,获取通讯录)
- location (获取定位)
- microphone (麦克风即话筒)
- phone(电话)
- sensors 传感器sms
-
storage 存储
2950322-ca99c0b1d1ab831b.png
看到没,这么多的权限都变成为危险权限了,都需要我们去特殊处理,还都是我们最常用的。
6.0 权限适配
早期做法
我们把 targetSDKVersion<23 的时候,会和4.X 版本一样,仅在安装时赋予权限,使用时将不被提醒,当targetSDKVersion≥23的时候才会使用新的运行时权限规则,但是这种并不是Google所推荐使用的。
compileSdkVersion 24
buildToolsVersion "24.0.2"
defaultConfig {
applicationId "com.whoislcj.rxpermissions"
minSdkVersion 15
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
实测这招可以,就是在我们使用 studio 打包安装时会提示安装失败,我们多装几次就行。
6.0 适配
google 已经给我们提供好线程的 API 啦,我们按照下面的套路来就行,话说现在社会真是套路的社会,去哪哪套路,敲代码也都是套路。
判断是否是Android 6.0以上
private boolean isSDK2API23() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
获取权限对应字符串
String permission = Manifest.permission.CAMERA;
我们去 Manifest 这个类里去找好了
检查用户是否给了改权限
private boolean isHavePermission(Context context, String permission) {
return PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(context, permission);
}
ContextCompat.checkSelfPermission,主要用于检测某个权限是否已经被授予,方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了。
申请权限
ActivityCompat.requestPermissions(this, new String[]{permission}, permissionRequestCode);
用户没给这个权限,判断是不是用户上次选择不再显示了
private boolean isUserNoShowPermissionDialig(Activity activity, String permission) {
return !(ActivityCompat.shouldShowRequestPermissionRationale(activity, permission));
}
为啥要用这个方法呢,因为用户在系统弹出的权限对话框中要是选了不再显示,那么我们再去申请这个权限的话,系统就不会弹出的权限对话框了,会直接替用户选择不给了。我们用这个方法可以判断出用户上次点没点不再显示这个选项,要是点了,那么我们通常的做法就是打开这个 app 对应的权限管理页,让用户去手动给这个权限。
注意这个方法返回 true 时,说明用户上次点了不再显示这个选项
打开这个 app 对应的权限管理页
private void startAppSettingActivity(Activity activity) {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + activity.getPackageName()));
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
activity.startActivityForResult(intent, 100);
}
接受用户权限选择的结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == permissionRequestCode) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, "您同意该权限", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "您不给该权限会出问题的", Toast.LENGTH_SHORT).show();
}
return;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
系统弹的权限对话框本质是一个 dialog 样式的 activity,使用 startActivityForResult 方法启动的,页面关闭返回一个记结果,所以我们只好重写 activity 的 onRequestPermissionsResult 方法
这点是我觉得系统最尼玛 SB 的设计,google 你就不能给自己一个全局弹窗的权限,真的谈一个 dialog 出来,多了 activity 的这个方法,代码怎么封装啊,这样的写法上太有侵入性了。
5.x 怎么适配
上面的做法是6.0以上的做法,但是国产好多手机在 5.X 上就集成了权限管理了,用刚刚的这段代码,你会发现用户就是不给这个权限,结果也是给了,实际是无法判断出来的。所以才会去限定 sdk 版本。但是我们真的要适配5.X 怎么办?
- 使用开源框架:permissions4m
- 使用系统的隐藏 api
AppOpsManager::checkOp(int op ,int uid ,String packageName) ,这个方法部分机器可能会 carsh ,注意 try/catch,找到5.1/5.1.1权限对应的 int 值,不同版本 int 值可能不同,这个号麻烦,具体的请看:Android Developer ,必知必会的权限管理知识
最后
最后啦,我把全部代码贴出来,这个写法只是让大家了解关于权限的基本 API,不是最终的写法, 之后我还写后续文章的
public class MainActivity extends AppCompatActivity {
int permissionRequestCode = 200;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void checkPermission(View view) {
if (!isSDK2API23()) {
return;
}
String permission = Manifest.permission.CAMERA;
if (!isHavePermission(this, permission)) {
if (isUserNoShowPermissionDialig(this, permission)) {
startAppSettingActivity(this);
} else {
ActivityCompat.requestPermissions(this, new String[]{permission}, permissionRequestCode);
}
} else {
Toast.makeText(MainActivity.this, "已获取该权限", Toast.LENGTH_SHORT).show();
}
}
private boolean isSDK2API23() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
private boolean isHavePermission(Context context, String permission) {
return PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(context, permission);
}
private boolean isUserNoShowPermissionDialig(Activity activity, String permission) {
return !(ActivityCompat.shouldShowRequestPermissionRationale(activity, permission));
}
private void startAppSettingActivity(Activity activity) {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + activity.getPackageName()));
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
activity.startActivityForResult(intent, 100);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == permissionRequestCode) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, "您同意该权限", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "您不给该权限会出问题的", Toast.LENGTH_SHORT).show();
}
return;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}