在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權限,而去進行寫存儲卡的操作的時候,就會引起應用的崩潰。
遇到一個坑,Android6.0運作時權限,拒絕了權限還傳回擷取成功
在紅米手機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的手機并不存在這個問題,點拒絕傳回的結果就是被拒絕,是以隻有部分手機可能存在這個問題,由于裝置有限,目前其它機型暫未測試