android 6.0运行时权限
Android 中的权限在6.0(API23)之前一直是在安装应用的时候一次性授予的,要不不安装此软件,要么接受此软件申请的所有权限。6.0之后,Google更改了权限的授予方式,用户在安装时可以选择授予或者不授予应用申请的权限,到需要使用此权限的时候再去向用户申请。
权限的两种类型
-
普通权限
普通权限指的是那些不涉及用户隐私的一些权限,比如连接网络,读取网络状态,比如去系统相册选择图片,再比如。
如果应用声明其需要正常权限,系统会自动向应用授予该权限,而不需要应用去向用户实时申请,同时,用户也无法取消授予这些权限。
下面的这些权限是被称为普通权限(PROTECTION_NORMAL)的
- ACCESS_LOCATION_EXTRA_COMMANDS
- ACCESS_NETWORK_STATE
- ACCESS_NOTIFICATION_POLICY
- ACCESS_WIFI_STATE
- BLUETOOTH
- BLUETOOTH_ADMIN
- BROADCAST_STICKY
- CHANGE_NETWORK_STATE
- CHANGE_WIFI_MULTICAST_STATE
- CHANGE_WIFI_STATE
- DISABLE_KEYGUARD
- EXPAND_STATUS_BAR
- GET_PACKAGE_SIZE
- INSTALL_SHORTCUT
- INTERNET
- KILL_BACKGROUND_PROCESSES
- MODIFY_AUDIO_SETTINGS
- NFC
- READ_SYNC_SETTINGS
- READ_SYNC_STATS
- RECEIVE_BOOT_COMPLETED
- REORDER_TASKS
- REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
- REQUEST_INSTALL_PACKAGES
- SET_ALARM
- SET_TIME_ZONE
- SET_WALLPAPER
- SET_WALLPAPER_HINTS
- TRANSMIT_IR
- UNINSTALL_SHORTCUT
- USE_FINGERPRINT
- VIBRATE
- WAKE_LOCK
- WRITE_SYNC_SETTINGS -
危险权限
危险权限涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如,能够读取用户的联系人属于危险权限,拨打电话属于危险权限,获取定位信息属于危险权限...如果应用声明其需要危险权限,则用户必须明确向应用授予该权限。这就需要动态向用户申请,也就是我们说的运行时权限申请。
这里还有一件事情要说明,就是权限组的概念。
####权限组
如果一个应用既需要写文件也需要读取文件,
既需要读取联系人也需要添加联系人,
既需要粗略定位也需要精确定位。
想象一下,如果不停地向用户去申请这些权限,用户是否会很反感。
所以,为了提供更好的用户体验,谷歌爸爸创造了一个权限组的概念!
权限组(这里只说危险权限,其实任何权限都可以属于一个权限组):
1. 任何一个危险权限都应该属于一个权限组,
2. 一旦应用申请的某个危险权限被用户授予,
3. 那么该权限组内的其他危险权限也被授予。
事实上,在你申请打电话权限的时候,系统弹框也是在询问用户是否授予该应用电话权限。电话权限的权限组包括打电话权限。
####9大权限组
- CALENDAR
- CAMERA
- CONTACTS
- LOCATION
- MICROPHONE
- PHONE
- SENSORS
- SMS
- STORAGE
[图片上传失败...(image-3b9dab-1522052782236)]
接下来我们来说说怎么申请运行时权限
1. 先说说几个系统api
-
checkSelfPermission
检查是否授予了某一项权限,
如果权限已经授予,返回0,否则返回-1
Determine whether you have been granted a particular permission. -
shouldShowRequestPermissionRationale
当你没有某个权限的时候,你最好显示一个UI组件,告诉用户这个应用为什么需要这个权限,告诉用户如果授予该权限会有带来什么好处,
如果您的权限请求已经被拒绝过一次,返回true,否则返回false
Gets whether you should show UI with rationale for requesting a permission. You should do this only if you do not have the permission and the context in which the permission is requested does not clearly communicate to the user what would be the benefit from granting this permission. -
requestPermissions
当检查到用户没有授予某项你需要的危险权限的时候,向用户申请该权限
Requests permissions to be granted to this application. These permissions must be requested in your manifest -
onRequestPermissionsResult
这是 requestPermissions 方法的回调,每次调用requestPermissions都会有一次回调
Callback for the result from requesting permissions. This method is invoked for every call on requestPermissions(String[], int).
2. 再说说怎么用
这里面有一个坑很多文章都没有讲明白,我也是在项目中遇到了才把这个问题给搞清楚了的。大多数文章的示例都是以Activity为背景讲的,像我这种刚接触运行时权限的菜鸟而言,当然就是copy下别人的用法喽,一不小心就掉进了坑里。
这个坑就是onRequestPermissionsResult方法根本没被调用。帅哥们看这里!
敲黑板划重点:
上面我们说的四个系统api中的后面三个方法其实是有两套的,Activity使用一套, Fragment要使用另外一套!在Activity中使用ActivityCompat.requestPermissions,在Fragment中使用requestPermissions
3. Talk is simple , show u the code!
我们来看示例代码:
看看Fragment怎么用
1.检查权限
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.CALL_PHONE)
== PackageManager.PERMISSION_GRANTED) {
//已经授权,
}else{
//没有权限
}
2.显示原因
if(shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE){
//如果需要显示请求权限的原因 在这里写
} else {
//请求权限
}
3.请求权限
requestPermissions(new String[]{Manifest.permission.CALL_PHONE},1);
4.处理回调
@Override
public void onRequestPermissionsResult(int requestCode,String permissions[], int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
一个完整的Fragment:
package com.example.francis.demo.simpletest;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.francis.demo.R;
public class RuntimePermissionFragment extends Fragment {
private final int REQUEST_CODE_CALL_PHONE = 1;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_runtime_permission, container, false);
view.findViewById(R.id.call_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
//打电话逻辑在哪里写
} else {
if (shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE)) {
//展示你给用户的解释,这不是必须的,如果你觉得不需要或者你不想解释,那就不要解释了
} else {
requestPermissions(new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE_CALL_PHONE);
}
}
}
});
return view;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_CALL_PHONE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//打电话权限被授予
//打电话逻辑写这里
} else {
//用户拒绝了你的打电话权限
}
}
}
}
看看Activity怎么用
1.检查权限
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.CALL_PHONE)
== PackageManager.PERMISSION_GRANTED) {
//已经授权,
}else{
//没有权限
}
2.显示原因
if(ActivityCompat.shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE){
//如果需要显示请求权限的原因 在这里写
} else {
//请求权限
}
3.请求权限
ActivityCompat.requestPermissions(new String[]{Manifest.permission.CALL_PHONE},1);
4.处理回调
@Override
public void onRequestPermissionsResult(int requestCode,String permissions[], int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
一个简单Activity示例
package com.example.francis.demo.simpletest;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import com.example.francis.demo.R;
public class RuntimePermissionActivity extends AppCompatActivity {
private final int REQUEST_CODE_CALL_PHONE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_runtime_permission);
findViewById(R.id.call_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ContextCompat.checkSelfPermission(RuntimePermissionActivity.this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
//打电话逻辑在哪里写
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(RuntimePermissionActivity.this,Manifest.permission.CALL_PHONE)) {
//展示你给用户的解释,这不是必须的,如果你觉得不需要或者你不想解释,那就不要解释了
} else {
//请求权限
ActivityCompat.requestPermissions(RuntimePermissionActivity.this,new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE_CALL_PHONE);
}
}
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_CALL_PHONE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//打电话权限被授予
//打电话逻辑写这里
} else {
//用户拒绝了你的打电话权限
}
}
}
}
小结
到了这里,运行时权限的申请流程和细节大家应该已经非常清楚了,接下来你需要做的就是自己写写看,看看源码介绍。虽然也有很多别人写好的很多库可以直接来调用,但是万变不离其宗,知道本质再去使用第三方库吧。
对了,别忘了在清单文件中声明这些危险权限哦...