为何google在6.0加入这个权限机制
android6.0之前其实也有权限机制,很简单就是开发者在manifest.xml注册,用户安装app时候,当做权限清单列出来告知用户我需要这些个权限,但是这样用户基本不会去看,导致app权限滥用造成安全隐患。
权限分类
Android权限有100多种不可能每种都去运行时授权,因此google把权限分为两类:
1.普通权限:例如网络请求等,按照老的权限机制
2.危险权限:9种共24个(电话,短信,sd卡,位置,摄像头,传感器,日历,录音,联系人),就是我们要动态申请的。
用adb命令查看危险权限列表:(tip:记住9种24类)
adb shell pm list permissions -d -g
Dangerous Permissions:
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
看到上面的dangerous permissions,会发现一个问题,好像危险权限都是一组一组的那么有个问题:分组对我们的权限机制有什么影响吗?的确是有影响的,如果app运行在Android 6.x的机器上,对于授权机制是这样的。如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。不过需要注意的是,不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。
怎么兼容
1.假设我们的项目之前没加这个权限机制,那现在我们怎么处理呢,要不要加这个权限判断,这个取决与app的版本兼容性也就是你们是否愿意为Android6.0以上的用户们处理这个问题。targetSdkVersion这个属性就是控制是否要引入权限机制的开关。
项目中targetSdkVersion<23: 按照老的权限机制,只在manifest声明就可以了,运行不会报错,但是会有隐患:
比如,我的APP有个拨号功能,把targetSdkVersion=21,安装到API23(Android6.0)的设备,可以正常拨号,看似正常。
但是用户可以直接去设置里面关掉权限:
image.png
我在模拟器上有这个警告提示,但不保证所有手机都会有这个提示!这时再打开APP就无法拨号了,由于没有检查权限,用户得不到任何提示,一脸懵逼。
targetSdkVersion>=23: 这时就需要在代码中检查权限了,否则打开app去执行需要权限的操作会崩溃。如果关闭权限将会弹出提示框提示你开启权限。
2.如果app原先的targetSdkVersion低于23,现在升级到targetSdkVersion=23的app,那么里面的权限默认全部开启!除非用户卸载重装这个app,才是默认关闭所有权限
关于读写SD卡权限
在6.0之前由于读写SD没有限制,导致sd卡目录被app滥用,于是google把sd卡续写权限列为危险权限,如果开发者不想申请sd卡读写权限,可以访问0/Android/data/包名 这个目录,不需要权限可以随便访问,Android也是建议开发者将这个目录作为app的缓存目录
使用
首先配置build.gradle,targetSdkVersion版本应该>=23,然后导入support-v4包:
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "com.example.carmelo.myapplication"
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-v4:23.2.1'
}
manifest.xml声明权限
在Activity或者Fragment中判断是否开启了这个权限,如果开启了就拨号,没有的话就申请权限:
界面弹出框:拒绝或者同意,回调两种结果:
[图片上传中。。。(2)]
public class MyActivity extends Activity {
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
btn = (Button) findViewById(R.id.textView);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int check = ContextCompat.checkSelfPermission(MyActivity.this, Manifest.permission.CALL_PHONE);
if(check== PackageManager.PERMISSION_GRANTED){
call();
}else {
ActivityCompat.requestPermissions(MyActivity.this
,new String[]{Manifest.permission.CALL_PHONE}
, 1);
}
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode==1){
if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
call();
}else {
Toast.makeText(MyActivity.this,"没有拨打电话权限",Toast.LENGTH_SHORT).show();
}
}
}
private void call(){
Intent intent = new Intent();
intent.setData(Uri.parse("tel://1212121212"));
intent.setAction(Intent.ACTION_CALL);
startActivity(intent);
}
}
不再提示的处理:
如果用户勾选了不再提示,并且拒绝了权限,那么后面不会再弹框,并且结果一直回调到没有权限,此时根据产品需求处理,一般2种方法:
在回调中弹出Dialog跳到设置界面开启权限
直接弹出提示:没有权限(这种更好),因为用户就是拒绝了权限,不要烦他了