前言

在Android 6.0(API 級別 23)以下申請權限是非常簡單的,直接在AndroidManifest.xml這個配置文件中加入申請權限的列表就可以了,比如我要申請四個權限,如下:

<!--打電話-->
<uses-permission android:name="android.permission.CALL_PHONE" />
<!--使用相機-->
<uses-permission android:name="android.permission.CAMERA" />
<!--寫入內存卡-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--讀取內存卡-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

但是在Android 6.0(API 級別 23)以上的版就不可以這樣申請了,因爲這樣申請權限對用戶來說是非常危險的,應用已安裝就獲取了全部權限,也不知道這些權限應用要來幹什麼,可能是用戶不希望發生的一些操作。所以在Android 6.0之後,一些危險的權限就要動態申請了,哪些是危險權限呢,下面是官方提供的一個需要動態申請的危險權限:

權限組 權限
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

如果要使用上面的權限,除了要在AndroidManifest.xml這個配置文件聲明,還要在Java代碼中增加動態申請。下面我們就介紹如何單個和多個權限動態申請。

單個權限的動態申請

比如我們的應用要打電話,打電話是一個危險權限.

  • 首先需要動態申請AndroidManifest.xml配置文件添加申請打電話權限的聲明,如下:
<!--打電話-->
<uses-permission android:name="android.permission.CALL_PHONE" />
  • 然後在Java代碼中編寫一個動態申請打電話權限的方法,當我們需要打電話之前,先要調用這個方法獲取權限:
// 請求單個權限
private void request_permission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)
            != PackageManager.PERMISSION_GRANTED) {
        // 最後的請求碼是對應回調方法的請求碼
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.CALL_PHONE}, 1001);
    } else {
        Toast.makeText(this, "你已經有權限了,可以直接撥打電話", Toast.LENGTH_LONG).show();
    }
}
  • 請求申請權限之後,當用戶同意或者拒絕權限之後,都會在請求權限的回調方法反饋,我們可以在這個回調方法中判斷是否已經授權,並做相關的操作:
// 請求權限回調方法
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case 1001:
            // 1001的請求碼對應的是申請打電話的權限
            // 判斷是否同意授權,PERMISSION_GRANTED 這個值代表的是已經獲取了權限
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(MainActivity.this, "你同意授權,可以打電話了", Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(MainActivity.this, "你不同意授權,不可以打電話", Toast.LENGTH_LONG).show();
            }
            break;
    }
}
  • 我們增加一個按鈕,讓這個按鈕的點擊事件調用我們的申請權限方法:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button request_permission_btn = (Button) findViewById(R.id.request_permission_btn);

    // 點擊獲取單個權限
    request_permission_btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            request_permission();
        }
    });
}
  • activity_main中的界面代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.yeyupiaoling.testpermission.MainActivity">

    <Button
        android:id="@+id/request_permission_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="請求單個權限" />
</LinearLayout>
  • 測試我們的程序,第二個按鈕時下一部分的,讀者目前可以不用理會。第一次申請的時候是沒有權限的,然後就會動態申請權限,用戶同意了就獲得了權限。第二次申請權限時,因爲第一次已經申請到了,所以可以直接使用了。

多個權限動態申請

  • 多個權限申請也是一樣的,首先同樣需要動態申請AndroidManifest.xml配置文件添加所有申請的權利,如下。值得注意的是WRITE_EXTERNAL_STORAGEREAD_EXTERNAL_STORAGE都是屬於STORAGE組的,在申請它們兩個的時候嗎,只要同意一個,系統會立即另外一個權限,不會再彈出權限授予詢問的對話框。
<!--使用相機-->
<uses-permission android:name="android.permission.CAMERA" />
<!--寫入內存卡-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--讀取內存卡-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  • 多個權限的申請就複雜一些,首先要先創建一個列表,把需要的申請的權限添加到這個列表中,最後統一提交申請:
// 請求多個權限
private void request_permissions() {
    // 創建一個權限列表,把需要使用而沒用授權的的權限存放在這裏
    List<String> permissionList = new ArrayList<>();

    // 判斷權限是否已經授予,沒有就把該權限添加到列表中
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED) {
        permissionList.add(Manifest.permission.CAMERA);
    }

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
    }

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
    }

    // 如果列表爲空,就是全部權限都獲取了,不用再次獲取了。不爲空就去申請權限
    if (!permissionList.isEmpty()) {
        ActivityCompat.requestPermissions(this,
                permissionList.toArray(new String[permissionList.size()]), 1002);
    } else {
        Toast.makeText(this, "多個權限你都有了,不用再次申請", Toast.LENGTH_LONG).show();
    }
}
  • 申請多個權限,在回調方法中也會反饋多個權限的申請結果,所以我們要判斷每個權限的申請結果,全部的權限都申請成功了,那纔是申請成功了:
// 請求權限回調方法
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case 1002:
            // 1002請求碼對應的是申請多個權限
            if (grantResults.length > 0) {
                // 因爲是多個權限,所以需要一個循環獲取每個權限的獲取情況
                for (int i = 0; i < grantResults.length; i++) {
                    // PERMISSION_DENIED 這個值代表是沒有授權,我們可以把被拒絕授權的權限顯示出來
                    if (grantResults[i] == PackageManager.PERMISSION_DENIED){
                        Toast.makeText(MainActivity.this, permissions[i] + "權限被拒絕了", Toast.LENGTH_SHORT).show();
                    }
                }
            }
            break;
    }
}
  • 我們同樣增加一個按鈕,讓這個按鈕的點擊事件調用我們的申請多個權限方法:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button request_permissions_btn = (Button) findViewById(R.id.request_permissions_btn);

    // 點擊獲取多個權限
    request_permissions_btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            request_permissions();
        }
    });
}
  • activity_main中的界面代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.yeyupiaoling.testpermission.MainActivity">

    <Button
        android:id="@+id/request_permissions_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="請求多個權限" />
</LinearLayout>
  • 效果展示,當我們點擊按鈕申請多個權限時,就會開始申請多個權限。如上面所說的讀取內存卡和寫入內存卡屬於一個組,所以我們在看到關於內存卡的只是申請一次,如果一個拒絕了,那就全部都拒絕了。

參考資料

  1. https://developer.android.com/guide/topics/security/permissions#normal-dangerous
小夜