Android 6.0 以后为了保护用户隐私,将一些权限的申请放在了应用运行的时候去申请, 比如以往的开发中,开发人员只需要将需要的权限在清单文件中配置即可,现在Google 可能想到用户可能并不注意这些权限。 so~ 就迎来了动态权限机制。
请注意,如果你使用了API 23以上, 然而并没有用代码处理权限问题。 那么你的程序将会 CRASH !!!
下面我们来看看到底哪些权限是需要动态申请的, 也称为危险权限。
危险权限
这类权限需要在需要的时候,需要我们动态申请,比如:当我们需要打开相机拍摄照片的时候需要我们通过代码的方式在需要的地方去申请权限。Android6.0中权限问题中我们需要注意的是:
1:由于权限API的问题,我们的Actiivty最好是AppCompatActivity类型的,也就是说在你的BaseActivity需要继承AppCompatActivity
2:权限是分组的,同一组的权限申请其中一个,同组的权限就全部都申请了
特殊权限 组:
CALENDAR 日历
CAMERA 相机
CONTACTS 联系人
LOCATION 定位
MICROPHONE 麦克相关,比如录音
PHONE 手机状态
SENSORS 传感器
SMS 短信
STORAGE 存储权限
具体的权限分组情况如下表:
以上这些权限不仅仅要在ManIfest.xml里面处理。 还要在代码中处理, 如不处理。将会CRASH, 可把SDK 换到23 自行尝试。
下面我们来演示一个打电话的Demo.
当点击一个按钮的时候,我们检查权限
//检查权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { //进入到这里代表没有权限. ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE); } else { callPhone(); }如果有权限,那么直接调用callPhone()方法去打电话, 否则就申请权限。 如果进入了申请权限代码,则会弹出一个弹框,让用户选择是否授权。
这里会有个回调方法,在FragmentActivity与Activity里都有该方法
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case REQUEST_CODE: if(grantResults.length >0 &&grantResults[0]==PackageManager.PERMISSION_GRANTED){ //用户同意授权 callPhone(); }else{ //用户拒绝授权 } break; } }
如果同意授权,那么会进入到第一个if 最后走进callPhone(), 如果点击取消。 那么说明用户拒绝了该权限。
这里还有个小问题,如果用户点击了不在提醒, 并拒绝之后。 那么下次点击该触发打电话的按钮 就会没有任何反应,体验非常的不好。 我们可以做如下处理。
public void call(View v) { //检查权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { //进入到这里代表没有权限. if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CALL_PHONE)){ //已经禁止提示了 Toast.makeText(MainActivity.this, "您已禁止该权限,需要重新开启。", Toast.LENGTH_SHORT).show(); }else{ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE); } } else { callPhone(); } }
这样的话,如果没有改权限。 会检查是否禁止提示, 如果禁止提示了, 我们给它一个Toast提示。
再给大家介绍一个比较常用到的——不能读取外部存储的权限问题,肯定很多小伙伴都碰到过。
直接上代码:
public static boolean isGrantExternalRW(Activity activity){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&activity.checkSelfPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) { activity.requestPermissions(new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE },1); return false; } return true; }
使用的时候直接调用就好了,如下:
isGrantExternalRW((Activity) context);//解决6.0的读取sdcard权限
还说上一个非常好的消息, 现在 在AS中, 如果我们用了危险权限 ,那么系统是会给提示的。 并且会生成相应代码哦~ 是不是非常的屌~~~
下面是对权限的总结:
EXPAND_STATUS_BAR 允许一个程序扩展收缩在状态栏,android开发网提示应该是一个类似Windows Mobile中的托盘程序
FACTORY_TEST 作为一个工厂测试程序,运行在root用户
FLASHLIGHT 访问闪光灯,android开发网提示HTC Dream不包含闪光灯
FORCE_BACK 允许程序强行一个后退操作是否在顶层activities
FOTA_UPDATE 暂时不了解这是做什么使用的,android开发网分析可能是一个预留权限.
GET_ACCOUNTS 访问一个帐户列表在Accounts Service中
GET_PACKAGE_SIZE 允许一个程序获取任何package占用空间容量
GET_TASKS 允许一个程序获取信息有关当前或最近运行的任务,一个缩略的任务状态,是否活动等等
HARDWARE_TEST 允许访问硬件
INJECT_EVENTS 允许一个程序截获用户事件如按键、触摸、轨迹球等等到一个时间流,android开发网提醒 算是hook技术吧INSTALL_PACKAGES允许一个程序 安装应用
INTERNAL_SYSTEM_WINDOW 允许打开窗口使用系统用户界面
ACCESS_CHECKIN_PROPERTIES 允许读写访问“properties”表在checkin数据库中,改值可以修改上传。
ACCESS_COARSE_LOCATION 允许一个程序访问CellID或WiFi热点来获取粗略的位置
ACCESS_FINE_LOCATION 允许一个程序访问精良位置(如GPS)
WRITE_CONTACTS 允许程序写入但不读取用户联系人数据
WRITE_GSERVICES 允许程序修改Google服务地图
WRITE_OWNER_DATA 允许一个程序写入但不读取所有者数据
WRITE_SETTINGS 允许程序读取或写入系统设置
WRITE_SMS 允许程序写短信
WRITE_SYNC_SETTINGS 允许程序写入同步设置
ACCESS_LOCATION_EXTRA_COMMANDS 允许应用程序访问额外的位置提供命令
ACCESS_MOCK_LOCATION 允许程序创建模拟位置提供用于测试
ACCESS_NETWORK_STATE 允许程序访问有关GSM网络信息
ACCESS_SURFACE_FLINGER 允许程序使用SurfaceFlinger底层特性
ACCESS_WIFI_STATE 允许程序访问Wi-Fi网络状态信息
ADD_SYSTEM_SERVICE 允许程序发布系统级服务
BATTERY_STATS 允许程序更新手机电池统计信息
BLUETOOTH 允许程序连接到已配对的蓝牙设备
BLUETOOTH_ADMIN 允许程序发现和配对蓝牙设备
BROADCAST_PACKAGE_REMOVED 允许程序广播一个提示消息在一个应用程序包已经移除后
BROADCAST_STICKY 允许一个程序广播常用intents
CALL_PHONE 允许一个程序初始化一个电话拨号不需通过拨号用户界面需要用户确认
DELETE_CACHE_FILES 允许程序删除缓存文件
DELETE_PACKAGES 允许一个程序删除包
DEVICE_POWER 允许访问底层电源管理
DIAGNOSTIC 允许程序RW诊断资源
DISABLE_KEYGUARD 允许程序禁用键盘锁
DUMP 允许程序返回状态抓取信息从系统服务
CALL_PRIVILEGED 允许一个程序拨打任何号码,包含紧急号码无需通过拨号用户界面需要用户确认
CAMERA 请求访问使用照相设备
CHANGE_COMPONENT_ENABLED_STATE 允许一个程序是否改变一个组件或其他的启用或禁用
CHANGE_CONFIGURATION 允许一个程序修改当前设置,如本地化
CHANGE_NETWORK_STATE 允许程序改变网络连接状态
CHANGE_WIFI_STATE 允许程序改变Wi-Fi连接状态
CLEAR_APP_CACHE 允许一个程序清楚缓存从所有安装的程序在设备中
CLEAR_APP_USER_DATA 允许一个程序清除用户设置
CONTROL_LOCATION_UPDATES 允许启用禁止位置更新提示从无线模块
REBOOT 请求能够重新启动设备
RECEIVE_BOOT_COMPLETED 允许一个程序接收到ACTION_BOOT_COMPLETED广播在系统完成启动
RECEIVE_MMS 允许一个程序监控将收到MMS彩信,记录或处理
RECEIVE_SMS 允许程序监控一个将收到短信息,记录或处理
RECEIVE_WAP_PUSH 允许程序监控将收到WAP PUSH信息
RECORD_AUDIO 允许程序录制音频
REORDER_TASKS 允许程序改变Z轴排列任务
RESTART_PACKAGES 允许程序重新启动其他程序
SEND_SMS 允许程序发送SMS短信
INTERNET 允许程序打开网络套接字
MANAGE_APP_TOKENS 允许程序管理(创建、催后、 z-order默认向z轴推移)程序引用在窗口管理器中
MASTER_CLEAR 目前还没有明确的解释,android开发网分析可能是清除一切数据,类似硬格机
MODIFY_AUDIO_SETTINGS 允许程序修改全局音频设置
MODIFY_PHONE_STATE 允许修改话机状态,如电源,人机接口等
MOUNT_UNMOUNT_FILESYSTEMS 允许挂载和反挂载文件系统可移动存储
PERSISTENT_ACTIVITY 允许一个程序设置他的activities显示
PROCESS_OUTGOING_CALLS 允许程序监视、修改有关播出电话
READ_CALENDAR 允许程序读取用户日历数据
READ_CONTACTS 允许程序读取用户联系人数据
READ_FRAME_BUFFER 允许程序屏幕波或和更多常规的访问帧缓冲数据
READ_INPUT_STATE 允许程序返回当前按键状态
READ_LOGS 允许程序读取底层系统日志文件
READ_OWNER_DATA 允许程序读取所有者数据
READ_SMS 允许程序读取短信息
READ_SYNC_SETTINGS 允许程序读取同步设置
READ_SYNC_STATS 允许程序读取同步状态
SET_ACTIVITY_WATCHER 允许程序监控或控制activities已经启动全局系统中
SET_ALWAYS_FINISH 允许程序控制是否活动间接完成在处于后台时
SET_ANIMATION_SCALE 修改全局信息比例
SET_DEBUG_APP 配置一个程序用于调试
SET_ORIENTATION 允许底层访问设置屏幕方向和实际旋转
SET_PREFERRED_APPLICATIONS 允许一个程序修改列表参数PackageManager.addPackageToPreferred()和removePackageFromPreferred()方法
SET_PROCESS_FOREGROUND 允许程序当前运行程序强行到前台
SET_PROCESS_LIMIT 允许设置最大的运行进程数量
SET_TIME_ZONE 允许程序设置时间区域
SET_WALLPAPER 允许程序设置壁纸
SET_WALLPAPER_HINTS 允许程序设置壁纸hits
SIGNAL_PERSISTENT_PROCESSES 允许程序请求发送信号到所有显示的进程中
STATUS_BAR 允许程序打开、关闭或禁用状态栏及图标
SUBSCRIBED_FEEDS_READ 允许一个程序访问订阅RSS Feed内容提供
SUBSCRIBED_FEEDS_WRITE 系统暂时保留改设置,android开发网认为未来版本会加入该功能。
SYSTEM_ALERT_WINDOW 允许一个程序打开窗口使用TYPE_SYSTEM_ALERT,显示在其他所有程序的顶层
VIBRATE 允许访问振动设备
WAKE_LOCK 允许使用PowerManager的 WakeLocks保持进程在休眠时从屏幕消失
WRITE_APN_SETTINGS 允许程序写入API设置
WRITE_CALENDAR 允许一个程序写入但不读取用户日历数据
可以看出Android中对资源以及服务的访问都非常严格,另外,在程序打包成APK的时候也需要对软件进行签名。
获取精确位置 ACCESS_FINE_LOCATION 通过GPS芯片接收卫星的定位信息,定位精度达10米以内
访问定位额外命令 ACCESS_LOCATION_EXTRA_COMMANDS 允许程序访问额外的定位提供者指令
获取模拟定位信息 ACCESS_MOCK_LOCATION 获取模拟定位信息,一般用于帮助开发者调试应用
获取网络状态 ACCESS_NETWORK_STATE 获取网络信息状态,如当前的网络连接是否有效
访问Surface Flinger ACCESS_SURFACE_FLINGER Android平台上底层的图形显示支持,一般用于游戏或照相机预览界面和底层模式的屏幕截图
获取WiFi状态 ACCESS_WIFI_STATE 获取当前WiFi接入的状态以及WLAN热点的信息
账户管理 ACCOUNT_MANAGER 获取账户验证信息,主要为GMail账户信息,只有系统级进程才能访问的权限
验证账户 AUTHENTICATE_ACCOUNTS 允许一个程序通过账户验证方式访问账户管理ACCOUNT_MANAGER相关信息
电量统计 BATTERY_STATS 获取电池电量统计信息
绑定小插件 BIND_APPWIDGET 允许一个程序告诉appWidget服务需要访问小插件的数据库,只有非常少的应用才用到此权限
绑定设备管理 BIND_DEVICE_ADMIN 请求系统管理员接收者receiver,只有系统才能使用
绑定输入法 BIND_INPUT_METHOD 请求InputMethodService服务,只有系统才能使用
绑定RemoteView BIND_REMOTEVIEWS 必须通过RemoteViewsService服务来请求,只有系统才能用
绑定壁纸 BIND_WALLPAPER 必须通过WallpaperService服务来请求,只有系统才能用
使用蓝牙 BLUETOOTH 允许程序连接配对过的蓝牙设备
蓝牙管理 BLUETOOTH_ADMIN 允许程序进行发现和配对新的蓝牙设备
变成砖头 BRICK 能够禁用手机,非常危险,顾名思义就是让手机变成砖头
应用删除时广播 BROADCAST_PACKAGE_REMOVED 当一个应用在删除时触发一个广播
收到短信时广播 BROADCAST_SMS 当收到短信时触发一个广播
连续广播 BROADCAST_STICKY 允许一个程序收到广播后快速收到下一个广播
WAP PUSH广播 BROADCAST_WAP_PUSH WAPPUSH服务收到后触发一个广播
拨打电话 CALL_PHONE 允许程序从非系统拨号器里输入电话号码
通话权限 CALL_PRIVILEGED 允许程序拨打电话,替换系统的拨号器界面
拍照权限 CAMERA 允许访问摄像头进行拍照
改变组件状态 CHANGE_COMPONENT_ENABLED_STATE 改变组件是否启用状态
改变配置 CHANGE_CONFIGURATION 允许当前应用改变配置,如定位
改变网络状态 CHANGE_NETWORK_STATE 改变网络状态如是否能联网
改变WiFi多播状态 CHANGE_WIFI_MULTICAST_STATE 改变WiFi多播状态
改变WiFi状态 CHANGE_WIFI_STATE 改变WiFi状态
清除应用缓存 CLEAR_APP_CACHE 清除应用缓存
清除用户数据 CLEAR_APP_USER_DATA 清除应用的用户数据
底层访问权限 CWJ_GROUP 允许CWJ账户组访问底层信息
手机优化大师扩展权限 CELL_PHONE_MASTER_EX 手机优化大师扩展权限
控制定位更新 CONTROL_LOCATION_UPDATES 允许获得移动网络定位信息改变
删除缓存文件 DELETE_CACHE_FILES 允许应用删除缓存文件
删除应用 DELETE_PACKAGES 允许程序删除应用
电源管理 DEVICE_POWER 允许访问底层电源管理
应用诊断 DIAGNOSTIC 允许程序到RW到诊断资源
禁用键盘锁 DISABLE_KEYGUARD 允许程序禁用键盘锁
转存系统信息 DUMP 允许程序获取系统dump信息从系统服务
状态栏控制 EXPAND_STATUS_BAR 允许程序扩展或收缩状态栏
工厂测试模式 FACTORY_TEST 允许程序运行工厂测试模式
使用闪光灯 FLASHLIGHT 允许访问闪光灯
强制后退 FORCE_BACK 允许程序强制使用back后退按键,无论Activity是否在顶层
访问账户Gmail列表 GET_ACCOUNTS 访问GMail账户列表
获取应用大小 GET_PACKAGE_SIZE 获取应用的文件大小
获取任务信息 GET_TASKS 允许程序获取当前或最近运行的应用
允许全局搜索 GLOBAL_SEARCH 允许程序使用全局搜索功能
硬件测试 HARDWARE_TEST 访问硬件辅助设备,用于硬件测试
注射事件 INJECT_EVENTS 允许访问本程序的底层事件,获取按键、轨迹球的事件流
安装定位提供 INSTALL_LOCATION_PROVIDER 安装定位提供
安装应用程序 INSTALL_PACKAGES 允许程序安装应用
内部系统窗口 INTERNAL_SYSTEM_WINDOW 允许程序打开内部窗口,不对第三方应用程序开放此权限
访问网络 INTERNET 访问网络连接,可能产生GPRS流量
结束后台进程 KILL_BACKGROUND_PROCESSES 允许程序调用killBackgroundProcesses(String).方法结束后台进程
管理账户 MANAGE_ACCOUNTS 允许程序管理AccountManager中的账户列表
普通权限的总结:
ACCESS_LOCATION_EXTRA_COMMANDS 定位权限
ACCESS_NETWORK_STATE 网络状态权限
ACCESS_NOTIFICATION_POLICY 通知 APP通知显示在状态栏
ACCESS_WIFI_STATE WiFi状态权限
BLUETOOTH 使用蓝牙权限
BLUETOOTH_ADMIN 控制蓝牙开关
BROADCAST_STICKY 粘性广播
CHANGE_NETWORK_STATE 改变网络状态
CHANGE_WIFI_MULTICAST_STATE 改变WiFi多播状态,应该是控制手机热点(猜测)
CHANGE_WIFI_STATE 控制WiFi开关,改变WiFi状态
DISABLE_KEYGUARD 改变键盘为不可用
EXPAND_STATUS_BAR 扩展bar的状态
GET_PACKAGE_SIZE 获取应用安装包大小
INTERNET 网络权限
KILL_BACKGROUND_PROCESSES 杀死后台进程
MODIFY_AUDIO_SETTINGS 改变音频输出设置
NFC 支付
READ_SYNC_SETTINGS 获取手机设置信息
READ_SYNC_STATS 数据统计
RECEIVE_BOOT_COMPLETED 监听启动广播
REORDER_TASKS 创建新栈
REQUEST_INSTALL_PACKAGES 安装应用程序
SET_TIME_ZONE 允许应用程序设置系统时间区域
SET_WALLPAPER 设置壁纸
SET_WALLPAPER_HINTS 设置壁纸上的提示信息,个性化语言
TRANSMIT_IR 红外发射
USE_FINGERPRINT 指纹识别
VIBRATE 震动
WAKE_LOCK 锁屏
WRITE_SYNC_SETTINGS 改变设置
SET_ALARM 设置警告提示
INSTALL_SHORTCUT 创建快捷方式
UNINSTALL_SHORTCUT 删除快捷方式
两个特殊权限
特殊权限,顾名思义,就是一些特别敏感的权限,在Android系统中,主要由两个
SYSTEM_ALERT_WINDOW,设置悬浮窗,进行一些黑科技
WRITE_SETTINGS 修改系统设置
关于上面两个特殊权限的授权,做法是使用startActivityForResult启动授权界面来完成。
请求SYSTEM_ALERT_WINDOW
private static final int REQUEST_CODE = 1;
private void requestAlertWindowPermission() {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) {
if (Settings.canDrawOverlays(this)) {
Log.i(LOGTAG, "onActivityResult granted");
}
}
}
上述代码需要注意的是
使用Action Settings.ACTION_MANAGE_OVERLAY_PERMISSION启动隐式Intent
使用"package:" + getPackageName()携带App的包名信息
使用Settings.canDrawOverlays方法判断授权结果
请求WRITE_SETTINGS
private static final int REQUEST_CODE_WRITE_SETTINGS = 2;
private void requestWriteSettings() {
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS );
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_WRITE_SETTINGS) {
if (Settings.System.canWrite(this)) {
Log.i(LOGTAG, "onActivityResult write settings granted" );
}
}
}
上述代码需要注意的是
使用Action Settings.ACTION_MANAGE_WRITE_SETTINGS 启动隐式Intent
使用"package:" + getPackageName()携带App的包名信息
使用Settings.System.canWrite方法检测授权结果
注意:关于这两个特殊权限,一般不建议应用申请。
一般项目中都是这样写的
动态申请权限的方法:
首先,需要在AndroidManifest.xml静态申请权限,否则无法动态申请权限,以下代码位置不能放错(在application之外):
<uses-permission android:name="android.permission.上表的权限字符" />
- 1
然后,在java代码中写动态申请权限的逻辑(这是官网的写法)
public void requestPower() {
//判断是否已经赋予权限
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.上表权限字符)
!= PackageManager.PERMISSION_GRANTED) {
//如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.上表权限字符)) {//这里可以写个对话框之类的项向用户解释为什么要申请权限,并在对话框的确认键后续再次申请权限
} else {
//申请权限,字符串数组内是一个或多个要申请的权限,1是申请权限结果的返回参数,在onRequestPermissionsResult可以得知申请结果
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.上表权限字符,}, 1);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
注意,因为判断语句,其实实际上当用户拒绝一次权限申请后,再次调用方法,不会再出现申请权限对话框。而如果不把权限写在数组里,而是每条权限放在一个多个以下方法里一起调用,实际上,每次一起调用这多个方法,实际只申请这些方法的一条权限。
4.判断权限申请结果的方法
判断权限申请结果的方法(在权限申请对话框消失后执行)(注意:PERMISSION_GRANTED变量,导入的是:
import static android.content.pm.PackageManager.PERMISSION_GRANTED):
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
for (int i = 0; i < permissions.length; i++) {
if (grantResults[i] == PERMISSION_GRANTED) {
Toast.makeText(this, "" + "权限" + permissions[i] + "申请成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "" + "权限" + permissions[i] + "申请失败", Toast.LENGTH_SHORT).show();
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
当系统要求用户授予权限时,用户可以选择指示系统不再要求提供该权限。这种情况下,无论应用在什么时候使用 requestPermissions() 再次要求该权限,系统都会立即拒绝此请求。
5.一个动态申请权限的简单例子(申请内存读写权限):
AndroidManifest.xml中:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- 1
- 2
java代码,在需要使用权限前调用:
public void requestAllPower() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
}
}
}
//小思路
private void initPermission() { // checkSelfPermission 检测有没有 权限 // PackageManager.PERMISSION_GRANTED 有权限 // PackageManager.PERMISSION_DENIED 拒绝权限 //检查权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { //进入到这里代表没有权限. // 请求授权 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE); } else { Toast.makeText(this, "你有权限", Toast.LENGTH_SHORT).show(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case REQUEST_CODE: if(grantResults.length >0 &&grantResults[0]==PackageManager.PERMISSION_GRANTED){ //用户同意授权 Toast.makeText(this, "用户同意授权", Toast.LENGTH_SHORT).show(); }else{ call();//这里主要是用户如果之前拒绝了然而点击这个功能没有效果 所以添加提示重启就可以重写申请权限 //用户拒绝授权 Toast.makeText(this, "用户拒绝授权", Toast.LENGTH_SHORT).show(); } break; } } public void call() { //检查权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { //进入到这里代表没有权限. if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CALL_PHONE)){ //已经禁止提示了 Toast.makeText(MainActivity.this, "您已禁止该权限,需要重新开启。", Toast.LENGTH_SHORT).show(); }else{ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE); } } else { // callPhone(); } } //解决6.0的读取sdcard权限 public static boolean isGrantExternalRW(Activity activity){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&activity.checkSelfPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) { activity.requestPermissions(new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE },1); return false; } return true; } //检查设备是否存在SDCard的工具方法 public static boolean hasSdcard() { String state = Environment.getExternalStorageState(); return state.equals(Environment.MEDIA_MOUNTED); }
第二种方式兼容旧版本
private void initPermission() { if (Build.VERSION.SDK_INT >= 23 && !isPermissionRequested) { String permissions[] = { Manifest.permission.INTERNET, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.MODIFY_AUDIO_SETTINGS, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.WRITE_SETTINGS, Manifest.permission.READ_PHONE_STATE, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.CHANGE_WIFI_STATE }; ArrayList<String> toApplyList = new ArrayList<String>(); for (String perm : permissions) { if (PackageManager.PERMISSION_GRANTED != checkSelfPermission(perm)) { toApplyList.add(perm); //进入到这里代表没有权限. } } if(toApplyList.size()==0) { return; } else { requestPermissions(toApplyList.toArray(new String[toApplyList.size()]), 0); } } }