Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android6.0 運行時權限簡單理解

Android6.0 運行時權限簡單理解

編輯:關於Android編程

6.0 運行時權限處理

在6.0以前 權限都是在安裝時授權的,如果用戶不授權就無法安裝;
Android從6.0(API 23)開始 使用運行時權限,而不是像以前那樣安裝時授權。當你需要某些權限時,系統會向用戶去申請權限。用戶可以隨時取消授權給你的權限。
6.0中權限分為兩類 普通權限和危險權限,普通權限在AndroidManifest 文件中注冊就可以得到,對於能獲得用戶隱私的權限屬於危險權限。在使用的時候必須用戶授權才能使用。例如 拍照,錄音 sd卡的操作,危險權限被分為很多組,只要一組中的其中一項被授權 Android 就會將這一組的權限打包都授權給你app

image

危險權限

危險權限被分為了9組

    Permission Group    Permissions

    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

普通權限

    ? ACCESS_LOCATION_EXTRA_COMMANDS  
    ? ACCESS_NETWORK_STATE
    ? ACCESS_NOTIFICATION_POLICY
    ? ACCESS_WIFI_STATE
    ? BLUETOOTH
    ? BLUETOOTH_ADMIN
    ? BROADCAST_STICKY
    ? CHANGE_NETWORK_STATE
    ? CHANGE_WIFI_MULTICAST_STATE
    ? CHANGE_WIFI_STATE
    ? DISABLE_KEYGUARD
    ? EXPAND_STATUS_BAR
    ? GET_PACKAGE_SIZE
    ? INSTALL_SHORTCUT
    ? INTERNET
    ? KILL_BACKGROUND_PROCESSES
    ? MODIFY_AUDIO_SETTINGS
    ? NFC
    ? READ_SYNC_SETTINGS
    ? READ_SYNC_STATS
    ? RECEIVE_BOOT_COMPLETED
    ? REORDER_TASKS
    ? REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
    ? REQUEST_INSTALL_PACKAGES
    ? SET_ALARM
    ? SET_TIME_ZONE
    ? SET_WALLPAPER
    ? SET_WALLPAPER_HINTS
    ? TRANSMIT_IR
    ? UNINSTALL_SHORTCUT
    ? USE_FINGERPRINT
    ? VIBRATE
    ? WAKE_LOCK
        ? WRITE_SYNC_SETTINGS

請求權限

targetSdkVerion

申請權限之前必須先說一下tartgetSdkVersion ,目標sdk版本,一般定義在build.gradle文件中。
如果 targetSDKVersion 是22 安裝好之後 Android系統就知道這個App在系統API22一下都測試過了並且能正確運行的,假如這個App運行在了Android6.0系統上,Android就會對這個App很”照顧“,兼容它正確運行。6.0系統會把App申請的權限都默認給這個App。
但是 ,在6.0系統 ,用戶可隨時撤銷授權給app的權限 ,即使系統默認都授權給你,用戶也可以取消掉。這時就沒權限了。所以即使是targetSDKVersion < 23 也不是就萬事大吉了。Android為我們提供了android.support.v4.content.PermissionChecker 來檢測是否具有某些權限

判斷 targetSdkVersion

/**
 * 檢查targetSDKVersion 是否在 23以上
 * @return
 */
private boolean checkTargetSdkVersion(){
    PackageInfo info= null;
    try {
        info = getPackageManager().getPackageInfo(getPackageName(),0);
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    int targetSdk=  info.applicationInfo.targetSdkVersion;
    log("TargetSdkVersion:"+targetSdk);
    if (targetSdk>=Build.VERSION_CODES.M){
        return true;
    }
    return false;
}

檢查權限

在去請求權限之前 應該先檢查一下系統 的版本 如果系統版本在6.0以上再去請求權限,如果不在就不去請求,直接使用

/**
 * 檢查系統版本是否在6.0或者6.0以上
 * @return 
 */
private boolean checkVersion(){
    // Build.VERSION.SDK_INT 當前系統版本
    //Build.VERSION_CODES.M 6.0版本
    if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.M){
        return true;
    }
    return false;
}

PermissionChecker.checkSelfPermission() 方法就是用於檢查App自身有沒有某一個權限 此方法適用於 targetSdkVersion < 23

context.checkSelfPermission() 適用於 targetSDKVersion >=23

返回結果有三種 狀態
- PermissionChecker.PERMISSION_GRANTED; //有權限
- PermissionChecker.PERMISSION_DENIED ; //無權限
- PermissionChecker.PERMISSION_DENIED_APP_OP;//無權限

PermissionChecker.PERMISSION_DENIED 和 PermissionChecker.PERMISSION_DENIED_APP_OP 的區別:
- targetSDKVersion 小於23沒有權限就返回 PermissionChecker.PERMISSION_DENIED_APP_OP
- targetSdkVersion23或者以上的返回 PermissionChecker.PERMISSION_DENIED

java
//檢測targetSDKVersion 是否在23以上
if (checkTargetSdkVersion()){
//targetSDKVersion >=23
//檢查是否具有讀取短信的權限
result = checkSelfPermission(permission);
}else{
//targetSDKVersion <23
//檢查是否具有讀取短信的權限
result= PermissionChecker.checkSelfPermission(this,permission);
}

請求權限

使用 requestPermissions() 方法去請求權限 參數有兩個 權限數組 和請求碼

requestPermissions(new String[]{"android.permission.READ_SMS"},10);

在請求權限之前最好是跟用戶解釋清楚為什麼要使用這個權限 ,用時候用戶並不清楚為什麼使用權限 就會被拒絕,如果一個權限被請求一次以上 在系統申請權限的Dialog會出現一個不再提醒的復選框 那怎麼判斷 用戶是否勾選了這個 不再提醒呢 ,Android提供了 shouldShowRequestPermissionRationale() 方法;

這個方法 在 第一次請求的時候 和 在用戶勾選了不再提醒時 返回false ,其他均返回true

// 第一次請求就返回false 拒絕過返回true 或者 用戶選擇不再提示返回false
boolean answer=  shouldShowRequestPermissionRationale(permission);
log("shouldShowRequestPermissionRationale :"+answer);
if (!answer){
    new AlertDialog.Builder(this).setTitle("權限說明")
            .setMessage("此功能需要讀取短信的權限,沒有權限無法使用此功能。請在稍後授權後使用")
            .setNegativeButton("確定", new DialogInterface.OnClickListener() {
                @RequiresApi(api = Build.VERSION_CODES.M)
                @Override
                public void onClick(DialogInterface dialog, int which) {

                    requestPermissions(new String[]{permission},SMS);

                }
            })
            .setNeutralButton("取消",null)
            .show();
}else{
    requestPermissions(new String[]{permission},SMS);

}

image


處理用戶響應

重寫 activity的 onRequestPermissionsResult() 的方法 處理權限的響應

權限的申請是可以多個權限一塊申請的 ,所以 響應結果也是 數組和 請求的權限數組對應

/**
 *  申請權限的響應
 * @param requestCode 請求碼
 * @param permissions 權限數組
 * @param grantResults 結果數組
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    switch (requestCode){
        case SMS:
          LogCheckResult(grantResults[0]);
            if (grantResults.length>0 && grantResults[0]==PermissionChecker.PERMISSION_GRANTED){
                //TODO 讀取短信
                Toast.makeText(this,"讀取短信授權成功",Toast.LENGTH_SHORT).show();
                tv.setText(getSmsInPhone());
            }else{
                Toast.makeText(this,"讀取短信授權失敗",Toast.LENGTH_SHORT).show();
            }
            break;


    }
}

完整的短信讀取權限申請 流程

請求權限

/**
 * 請求短信權限
 */
@RequiresApi(api = Build.VERSION_CODES.M)
private void requestSms() {
    // 權限
        final String permission = "android.permission.READ_SMS";
    //檢查當前系統版本是否在6.0以上
    if (checkVersion()){
        int result =-1;
        //檢測targetSDKVersion 是否在23以上
            if (checkTargetSdkVersion()){
                //targetSDKVersion >=23
                //檢查是否具有讀取短信的權限
                result = checkSelfPermission(permission);
            }else{
                //targetSDKVersion <23
                //檢查是否具有讀取短信的權限
                result= PermissionChecker.checkSelfPermission(this,permission);
            }
            LogCheckResult(result);
            if(result==PermissionChecker.PERMISSION_GRANTED){
                //已經有了權限
                //TODO 讀取短信
                Toast.makeText(this,"讀取短信授權成功",Toast.LENGTH_SHORT).show();
                tv.setText(getSmsInPhone());
            }else{
                //沒有權限
                //TODO 請求權限
                // 第一次請求就返回false 拒絕過返回true 或者 用戶選擇不再提示返回false
              boolean answer=  shouldShowRequestPermissionRationale(permission);
                log("shouldShowRequestPermissionRationale :"+answer);
                if (!answer){
                    new AlertDialog.Builder(this).setTitle("權限說明")
                            .setMessage("此功能需要讀取短信的權限,沒有權限無法使用此功能。請在稍後授權後使用")
                            .setNegativeButton("確定", new DialogInterface.OnClickListener() {
                                @RequiresApi(api = Build.VERSION_CODES.M)
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    requestPermissions(new String[]{permission},SMS);
                                }
                            })
                            .setNeutralButton("取消",null)
                            .show();
                }else{
                    requestPermissions(new String[]{permission},SMS);
                }
            }
    }else{
        //無需請求
        Toast.makeText(this,"讀取短信授權成功",Toast.LENGTH_SHORT).show();
        tv.setText(getSmsInPhone());
    }
}

響應處理

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    switch (requestCode){
        case SMS:
          LogCheckResult(grantResults[0]);
            if (grantResults.length>0 && grantResults[0]==PermissionChecker.PERMISSION_GRANTED){
                //TODO 讀取短信
                Toast.makeText(this,"讀取短信授權成功",Toast.LENGTH_SHORT).show();
                tv.setText(getSmsInPhone());
            }else{
                Toast.makeText(this,"讀取短信授權失敗",Toast.LENGTH_SHORT).show();
            }
            break;


    }
}

短信讀取 代碼

    public String getSmsInPhone() {
        log("開始讀取短信");
        final String SMS_URI_ALL = "content://sms/";
        final String SMS_URI_INBOX = "content://sms/inbox";
        final String SMS_URI_SEND = "content://sms/sent";
        final String SMS_URI_DRAFT = "content://sms/draft";
        final String SMS_URI_OUTBOX = "content://sms/outbox";
        final String SMS_URI_FAILED = "content://sms/failed";
        final String SMS_URI_QUEUED = "content://sms/queued";

        StringBuilder smsBuilder = new StringBuilder();

        try {
            Uri uri = Uri.parse(SMS_URI_ALL);
            String[] projection = new String[] { "_id", "address", "person", "body", "date", "type" };
            Cursor cur = getContentResolver().query(uri, projection, null, null, "date desc");      // 獲取手機內部短信
            log("cursor:"+cur.getCount());
            if (cur.moveToFirst()) {
                int index_Address = cur.getColumnIndex("address");
                int index_Person = cur.getColumnIndex("person");
                int index_Body = cur.getColumnIndex("body");
                int index_Date = cur.getColumnIndex("date");
                int index_Type = cur.getColumnIndex("type");

                do {
                    String strAddress = cur.getString(index_Address);
                    int intPerson = cur.getInt(index_Person);
                    String strbody = cur.getString(index_Body);
                    long longDate = cur.getLong(index_Date);
                    int intType = cur.getInt(index_Type);

                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
                    Date d = new Date(longDate);
                    String strDate = dateFormat.format(d);

                    String strType = "";
                    if (intType == 1) {
                        strType = "接收";
                    } else if (intType == 2) {
                        strType = "發送";
                    } else {
                        strType = "null";
                    }

                    smsBuilder.append("[ ");
                    smsBuilder.append(strAddress + ", ");
                    smsBuilder.append(intPerson + ", ");
                    smsBuilder.append(strbody + ", ");
                    smsBuilder.append(strDate + ", ");
                    smsBuilder.append(strType);
                    smsBuilder.append(" ]\n\n");
                } while (cur.moveToNext());

                if (!cur.isClosed()) {
                    cur.close();
                    cur = null;
                }
            } else {
                smsBuilder.append("no result!");
            } // end if

            smsBuilder.append("getSmsInPhone has executed!");

        } catch (SQLiteException ex) {
            log("SQLiteException in getSmsInPhone");
        }

        return smsBuilder.toString();
    }

關於這次的Demo,github 地址: https://github.com/sky-mxc/AndroidDemo/tree/master/permission

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved