在Android M(6.0)以前使用某权限是不需要用户授权的,只要在Manifest中注册即可,在Android M之后需要注册并申请用户授权,以下是一些敏感权限需要在使用时动态申请,这些权限大概如下图:
首先了解一下权限申请的api分界线android 6.0(API=23),如果targetSdkVersion < 23 ,这种情况下敏感权限是全部授权的;但是当你的targetSdkVersion >= 23 时,这时如果再次用到上面这些敏感权限,那么就需要动态申请了;假设你的targetSdkVersion 设定28,那么在系统API为28的手机上 就用28的api(用到敏感权限就要申请) , 如果你设定的是20,那么在28手机上面 就使用的是20的api (低于23,全新不需要申请) ,这就是高版本系统的向下兼容性
下面就是一个例子,我设置的targetSdkVersion为28,用到权限READ_PHONE_STATE,这个权限是敏感权限,那么使用时就要申请:build.gradle文件如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
applicationId "com.xxxxx"
minSdkVersion 14
targetSdkVersion 28
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
lintOptions {
checkReleaseBuilds false
abortOnError false
}
android{
useLibrary'org.apache.http.legacy'
}
}
dependencies {
compile files('libs/commons-logging-1.1.1.jar')
// compile 'com.android.support:appcompat-v7:24.2.1'
// implementation 'androidx.annotation:annotation:1.0.2'
// compile 'com.android.support:support-v4:24.2.1'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
}
动态权限继承的方法是onRequestPermissionsResult,该方法是AppCompatActivity才有,所以必须要有appcompat-v7这个依赖包,而AppCompatActivity从24.2.1有的,低于24的几个版本试过有的不存在这个activity
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.PermissionChecker;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private final int READ_CODE = 10;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.activity_main2);
checkPermission();
}
private void checkPermission(){
try{
// if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (PermissionChecker.checkSelfPermission(MainActivity.this, Manifest.permission.READ_PHONE_STATE) != PermissionChecker.PERMISSION_GRANTED) {
// 不相等 请求授权
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_PHONE_STATE}, READ_CODE);
} else {
Toast.makeText(this, "已授权", Toast.LENGTH_SHORT).show();
}
}else{
}
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case READ_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "onRequestPermissionsResult: " +
"权限申请成功");
for (int i : grantResults) {
Log.d(TAG, "onRequestPermissionsResult: " + i);
}
Toast.makeText(this, "用户申请权限成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "您已拒绝申请权限", Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
}
说明一点,看以上第一章图的权限组,在 Android O (8.0)之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用,对于针对Android O的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准,但是若没有请求相应的权限而进行操作的话就会出现应用crash的情况.
例如,假设某个应用在其清单中列出READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE。应用请求READ_EXTERNAL_STORAGE,并且用户授予了该权限,如果该应用针对的是API级别24或更低级别,系统还会同时授予WRITE_EXTERNAL_STORAGE,因为该权限也属于STORAGE权限组并且也在清单中注册过。如果该应用针对的是Android O,则系统此时仅会授予READ_EXTERNAL_STORAGE,不过在该应用以后申请WRITE_EXTERNAL_STORAGE权限时,系统会立即授予该权限,而不会提示用户。但是若没有申请WRITE_EXTERNAL_STORAGE权限,而去进行写存储卡的操作的时候,就会引起应用的崩溃。
在红米手机6.0.1的手机上测试,app安装默认是 授权的,搞不懂,然后在安全中心——应用权限管理——你的应用,将权限修改为拒绝,在打开app是返回拒绝申请,这是ok的;然后将权限修改为询问,重新打开app提示权限弹框,点击拒绝,这时问题就来了,onRequestPermissionsResult函数的回调结果竟然是 PERMISSION_GRANTED,也就是权限申请成功,但是系统的权限明显是拒绝了,所以在回调结果的权限申请成功这里要加一次判断确定是否真的成功,如下:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case READ_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "onRequestPermissionsResult: " +
"权限申请成功");
//部分手机如红米6.0.1系统权限弹框点拒绝回调结果是成功,所以这里要再次判断是否真的具有权限
if (PermissionChecker.checkSelfPermission(MainActivity.this, Manifest.permission.READ_PHONE_STATE) != PermissionChecker.PERMISSION_GRANTED) {
Toast.makeText(this, "系统是已拒绝申请权限", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "用户申请权限成功", Toast.LENGTH_SHORT).show();
doInit();
}
} else {
Toast.makeText(this, "您已拒绝申请权限", Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
有的说是第三方厂商各种改,返回的状态不正常,上面的方法可以解决这个问题,在红米手机是有效的,而华为8.0的手机并不存在这个问题,点拒绝返回的结果就是被拒绝,所以只有部分手机可能存在这个问题,由于设备有限,目前其它机型暂未测试