Android動態權限申請


從Android 6.0開始,權限不再是在Manifest文件中聲明一下即可申請,除了要在Manifest中填寫以外,還要在程序中動態申請,即便用戶授權,權限也不是永久的。

Android系統權限的概念:

Android是一個權限分隔的操作系統,每個應用都有獨特的系統標識。一般情況下,應用沒有權限執行對其它應用、系統、用戶可能有不利影響的操作。每個應用都在應用沙盒中運行,因此當應用需要使用沙盒未提供的功能時,需要申請權限,比如讀寫sd卡、訪問網絡、訪問其它應用的數據、讀寫聯系人、調用攝像頭等。權限在Manifest.xml文件中聲明,Android 6.0以前,有的APP一股腦聲明了各種各樣的權限,用戶可能沒有細看就安裝了,於是這些APP就可以為所欲為,無法無天。Android 6.0把權限分成正常權限和危險權限,AndroidManifest中聲明的正常權限系統會自動授予,而危險權限則需要在使用的時候用戶明確授予。就是Android 6.0以上的系統在第一次使用危險權限的時候,需要向用戶申請,征得用戶的同意。如果還是在沒有權限的情況下執行操作就會Crash,錯誤日志為java.lang.SecurityException: Permission Denial。因此,應用對危險權限的申請,需要相應的處理。

危險權限和對應的權限組

危險權限都屬於權限組,應用在向用戶申請危險權限時,系統會彈對話框,描述應用要訪問的權限組,這時候用戶如果同意授權,則權限組包含的所有權限都會被系統授予。比如,應用被授予READ_EXTERNAL_STORAGE權限之后,如果再申請WRITE_EXTERNAL_STORAGE權限,系統會立即授予該權限。

危險權限表格如下

權限組 權限
CALENDAR READ_CALENDAR
WRITE_CALENDAR
CAMERA CAMERA
CONTACTS READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
LOCATION  ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE READ_PHONE_STATE
CALL_PHONE
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
SENSORS BODY_SENSORS
SMS SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
STORAGE READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE

 

具體的使用代碼如下:

android.permission-group.CALENDAR
                          - android.permission.READ_CALENDAR
                          - android.permission.WRITE_CALENDAR

android.permission-group.CALL_LOG
                          - android.permission.READ_CALL_LOG
                          - android.permission.WRITE_CALL_LOG
                          - android.permission.PROCESS_OUTGOING_CALLS

android.permission-group.CAMERA
                          - android.permission.CAMERA

android.permission-group.CONTACTS
                          - android.permission.READ_CONTACTS
                          - android.permission.WRITE_CONTACTS
                          - android.permission.GET_ACCOUNTS

android.permission-group.LOCATION
                          - android.permission.ACCESS_FINE_LOCATION
                          - android.permission.ACCESS_COARSE_LOCATION

android.permission-group.MICROPHONE
                          - android.permission.RECORD_AUDIO

android.permission-group.PHONE
                          - android.permission.READ_PHONE_STATE
                          - android.permission.READ_PHONE_NUMBERS
                          - android.permission.CALL_PHONE
                          - android.permission.ANSWER_PHONE_CALLS
                          - com.android.voicemail.permission.ADD_VOICEMAIL
                          - android.permission.USE_SIP

android.permission-group.SENSORS
                          - android.permission.BODY_SENSORS

android.permission-group.SMS
                          - android.permission.SEND_SMS
                          - android.permission.RECEIVE_SMS
                          - android.permission.READ_SMS
                          - android.permission.RECEIVE_WAP_PUSH
                          - android.permission.RECEIVE_MMS

android.permission-group.STORAGE
                          - android.permission.READ_EXTERNAL_STORAGE
                          - android.permission.WRITE_EXTERNAL_STORAGE

 

申請權限

首先在manifest文件中聲明

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

 使用時檢查權限,沒有權限則申請

 

判斷是否擁有權限,使用ActivityCompat類中的checkSelfPermission方法,僅支持檢測單個權限,如果需要檢測多個權限,則使用邏輯運算符實現,這里以檢測是否具有寫存儲空間權限和相機權限為例:
if (!(ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) || !(ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)) { //沒有權限,申請權限 }else { //擁有權限 }

 

在檢測到沒有權限的時候,使用ActivityCompat中的requestPermissions申請權限,這里同樣是使用上面例子

String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}; //申請權限,其中RC_PERMISSION是權限申請碼,用來標志權限申請的 ActivityCompat.requestPermissions(MainActivity.this,permissions, RC_PERMISSION);

 

申請權限的結果我們可以在onRequestPermissionsResult方法中進行分析

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

      if (requestCode == RC_PERMISSION && grantResults.length == 2
              && grantResults[0] == PackageManager.PERMISSION_GRANTED
              && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
          Log.e(TAG, "權限申請成功");
      }else {
          Log.e(TAG, "權限申請失敗");
      }
}

參數解釋:

  • requestCode:權限申請碼,標志是哪次權限申請
  • permissions:申請權限的字符串數組
  • grantResults:權限申請的結果,是一個整型數組,這個數組的長度就是申請權限的個數,並且數組元素就是對應每個權限的申請結果
使用兼容庫
EasyPermissions是Google官方推薦的簡化權限申請的第三方框架,下面簡單介紹該框架的使用:
首先先在項目中添加依賴:
implementation 'pub.devrel:easypermissions:2.0.0'

 檢查是否擁有權限

 
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                        Manifest.permission.CAMERA};
boolean hasPermissions = EasyPermissions.hasPermissions(EasyPermissionActivity.this, permissions);
if (hasPermissions) {
     //擁有權限
}else {
    //沒有權限
}

申請權限

  • 首先重寫onRequestPermissionsResult方法,將權限申請結果交給EasyPermissions處理
@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //Forward results to EasyPermissions
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }

 

  • 讓Activity或者是Fragment實現EasyPermissions.PermissionCallbacks接口,實現這個接口的onPermissionsGranted和onPermissionsDenied方法,這兩個方法是EasyPermissions處理權限申請結果的回調
 @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {

    }

    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            //用戶勾選了“不再詢問”,引導用戶去設置頁面打開權限
            new AppSettingsDialog.Builder(this)
                    .setTitle("權限申請")
                    .setRationale("應用程序運行缺少必要的權限,請前往設置頁面打開")
                    .setPositiveButton("去設置")
                    .setNegativeButton("取消")
                    .setRequestCode(RC_SETTINGS_SCREEN)
                    .build()
                    .show();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == RC_SETTINGS_SCREEN) {
            //用戶從設置頁面返回,可以在這里檢測用戶是否打開了權限
        }
    }

 

  • 在需要申請權限的地方調用以下方法
@AfterPermissionGranted(RC_PERMISSIONS)
private void requestStoreAndCameraPermission() {
        String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA};
        if (EasyPermissions.hasPermissions(this, permissions)) {
            //權限獲取成功
        }else {
            //沒有權限,調用方法申請權限
            EasyPermissions.requestPermissions(this, "程序運行需要存儲權限和相機權限", RC_PERMISSIONS, permissions);
        }
    }

 

添加AfterPermissionGranted注解的作用是,當權限申請成功之后,系統會自動調用一次該方法,這樣會直接走權限獲取成功的業務邏輯;
EasyPermissions.requestPermissions方法的第二個參數的意義是當用戶第一次拒絕了權限之后,第二次調用該方法獲取權限時,會先彈出一個對話框向用戶解釋為何需要該權限,對話框的內容就是這個參數(彈出的對話框樣式在這里無法自己定義

RxPermissions也是一個替代方案,而且該框架提供了處理用戶拒絕授權並且勾選了“不在詢問”后的處理方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM