Android6.0動態申請權限那些坑--以及避免用戶選擇不再提示后無法獲取權限的問題


Android 6.0 為了保護用戶隱私,將一些權限的申請放在了應用運行的時候去申請, 比如以往的開發中,開發人員只需要將需要的權限在清單文件中配置即可,安裝后用戶可以在設置中的應用信息中看到:XX應用以獲取****權限。用戶點擊可以選擇給應用相應的權限。此前的應用權限用戶可以選擇允許、提醒和拒絕。在安裝的時候用戶是已經知道應用需要的權限的。但是這樣存在一個問題,就是用戶在安裝的時候,應用需要的權限十分的多(有些開發者為了省事,會請求一些不必要的權限或者請求全部的權限),這個時候用戶在安裝應用的時候也許並沒有發現某些侵犯自己隱私的權限請求,安裝之后才發現自己的隱私數據被竊取。其實Android6.0 動態權限一方面是為了廣大用戶考慮,另一方面其實是Google為了避免一些不必要的官司。下面就說一下Android6.0對權限的分割:


這類權限需要在需要的時候,需要我們動態申請,比如:當我們需要打開相機拍攝照片的時候需要我們通過代碼的方式在需要的地方去申請權限。Android6.0中權限問題中我們需要注意的是:

具體的權限分組情況如下表:

 
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
 
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
 
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
 
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
 
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
 
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
 
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
 
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
 
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
 
普通權限的總結:
 
ACCESS_LOCATION_EXTRA_COMMANDS 定位權限
 
ACCESS_NETWORK_STATE 網絡狀態權限
 
ACCESS_NOTIFICATION_POLICY 通知 APP通知顯示在狀態欄
 
ACCESS_WIFI_STATE WiFi狀態權限
 
BLUETOOTH 使用藍牙權限
 
BLUETOOTH_ADMIN 控制藍牙開關
 
BROADCAST_STICKY 粘性廣播
 
CHANGE_NETWORK_STATE 改變網絡狀態
 
CHANGE_WIFI_MULTICAST_STATE 改變WiFi多播狀態,應該是控制手機熱點(猜測)
 
CHANGE_WIFI_STATE 控制WiFi開關,改變WiFi狀態
 
DISABLE_KEYGUARD 改變鍵盤為不可用
 
EXPAND_STATUS_BAR 擴展bar的狀態
 
GET_PACKAGE_SIZE 獲取應用安裝包大小
 
INTERNET 網絡權限
 
KILL_BACKGROUND_PROCESSES 殺死后台進程
 
MODIFY_AUDIO_SETTINGS 改變音頻輸出設置
 
NFC 支付
 
READ_SYNC_SETTINGS 獲取手機設置信息
 
READ_SYNC_STATS 數據統計
 
RECEIVE_BOOT_COMPLETED 監聽啟動廣播
 
REORDER_TASKS 創建新棧
 
REQUEST_INSTALL_PACKAGES 安裝應用程序
 
SET_TIME_ZONE 允許應用程序設置系統時間區域
 
SET_WALLPAPER 設置壁紙
 
SET_WALLPAPER_HINTS 設置壁紙上的提示信息,個性化語言
 
TRANSMIT_IR 紅外發射
 
USE_FINGERPRINT 指紋識別
 
VIBRATE 震動
 
WAKE_LOCK 鎖屏
 
WRITE_SYNC_SETTINGS 改變設置
 
SET_ALARM 設置警告提示
 
INSTALL_SHORTCUT 創建快捷方式
 
UNINSTALL_SHORTCUT 刪除快捷方式
 
以上這些只是普通權限,我們開發的時候,正常使用就行了,需要的權限在清單文件配置即可。

申請步驟

    1. 將targetSdkVersion設置為23,注意,如果你將targetSdkVersion設置為>=23,則必須按照Android谷歌的要求,動態的申請權限,如果你暫時不打算支持動態權限申請,則targetSdkVersion最大只能設置為22.
  • 2 在AndroidManifest.xml中申請你需要的權限,包括普通權限和需要申請的特殊權限。

  • 3.開始申請權限,此處分為3部。

    • (1)檢查是否由此權限checkSelfPermission(),如果已經開啟,則直接做你想做的。

    • (2)如果未開啟,則判斷是否需要向用戶解釋為何申請權限shouldShowRequestPermissionRationale。

    • (3)如果需要(即返回true),則可以彈出對話框提示用戶申請權限原因,用戶確認后申請權限requestPermissions(),如果不需要(即返回false),則直接申請權限requestPermissions()。
單個權限申請.png
/**
* Requests permission.
*
* @param activity
* @param requestCode request code, e.g. if you need request CAMERA permission,parameters is PermissionUtils.CODE_CAMERA
*/
public static void requestPermission(final Activity activity, final int requestCode, PermissionGrant permissionGrant) {
if (activity == null) {
return;
}
 
Log.i(TAG, "requestPermission requestCode:" + requestCode);
if (requestCode < 0 || requestCode >= requestPermissions.length) {
Log.w(TAG, "requestPermission illegal requestCode:" + requestCode);
return;
}
 
final String requestPermission = requestPermissions[requestCode];
 
//如果是6.0以下的手機,ActivityCompat.checkSelfPermission()會始終等於PERMISSION_GRANTED,
// 但是,如果用戶關閉了你申請的權限(如下圖,在安裝的時候,將一些權限關閉了),ActivityCompat.checkSelfPermission()則可能會導致程序崩潰(java.lang.RuntimeException: Unknown exception code: 1 msg null),
// 你可以使用try{}catch(){},處理異常,也可以判斷系統版本,低於23就不申請權限,直接做你想做的。permissionGrant.onPermissionGranted(requestCode);
// if (Build.VERSION.SDK_INT < 23) {
// permissionGrant.onPermissionGranted(requestCode);
// return;
// }
 
int checkSelfPermission;
try {
checkSelfPermission = ActivityCompat.checkSelfPermission(activity, requestPermission);
} catch (RuntimeException e) {
Toast.makeText(activity, "please open this permission", Toast.LENGTH_SHORT)
.show();
Log.e(TAG, "RuntimeException:" + e.getMessage());
return;
}
 
if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED");
 
 
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission)) {
Log.i(TAG, "requestPermission shouldShowRequestPermissionRationale");
shouldShowRationale(activity, requestCode, requestPermission);
 
} else {
Log.d(TAG, "requestCameraPermission else");
ActivityCompat.requestPermissions(activity, new String[]{requestPermission}, requestCode);
}
 
} else {
Log.d(TAG, "ActivityCompat.checkSelfPermission ==== PackageManager.PERMISSION_GRANTED");
Toast.makeText(activity, "opened:" + requestPermissions[requestCode], Toast.LENGTH_SHORT).show();
//得到權限的時候,就可以在回調里面做你想做的事情了
permissionGrant.onPermissionGranted(requestCode);
}
}
 
備注!!!
1checkSelfPermission:檢查是否擁有這個權限
2requestPermissions:請求權限,一般會彈出一個系統對話框,詢問用戶是否開啟這個權限。
3shouldShowRequestPermissionRationaleAndroid原生系統中,如果第二次彈出權限申請的對話框,會出現“以后不再彈出”的提示框,如果用戶勾選了,你再申請權限, 則shouldShowRequestPermissionRationale返回true,意思是說要給用戶一個 解釋,告訴用戶為什么要這個權限。然而,在實際開發中,需要注意的是,很多手機對原生 系統做了修改,比如小米,小米46.0shouldShowRequestPermissionRationale 就一直返回false,而且在申請權限時,如果用戶選擇了拒絕,則不會再彈出對話框了 。。。。 所以說這個地方有坑,我的解決方法是,在回調里面處理,如果用戶拒絕了這個權限,則打開本應用信息界面,由用戶自己手動開啟這個權限。
4)每個應用都有自己的權限管理界面,里面有本應用申請的權限以及各種狀態,即使用戶已經同意了你申請的權限,他也隨時可以關閉
 
 

注意事項

API問題

由於checkSelfPermission和requestPermissions從API 23才加入,低於23版本,需要在運行時判斷 或者使用Support Library v4中提供的方法

  • ContextCompat.checkSelfPermission

  • ActivityCompat.requestPermissions

  • ActivityCompat.shouldShowRequestPermissionRationale

多系統問題

當我們支持了6.0必須也要支持4.4,5.0這些系統,所以需要在很多情況下,需要有兩套處理。比如Camera權限

[java] copy ?
  1. if  
  2.  {  
  3.   
  4.  

     

    兩個特殊權限

    特殊權限,顧名思義,就是一些特別敏感的權限,在Android系統中,主要由兩個

    • SYSTEM_ALERT_WINDOW,設置懸浮窗,進行一些黑科技

    • WRITE_SETTINGS 修改系統設置

    關於上面兩個特殊權限的授權,做法是使用startActivityForResult啟動授權界面來完成。

    請求SYSTEM_ALERT_WINDOW

    [java] copy ?
    1. private   REQUEST_CODE = ;  
    2. private requestAlertWindowPermission() {  
    3.  Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);  
    4.  + getPackageName()));  
    5. @Overrideprotected onActivityResult( requestCode,  resultCode, Intent data) {  
    6. .onActivityResult(requestCode, resultCode, data);  
    7.  (requestCode == REQUEST_CODE) {  
    8.  (Settings.canDrawOverlays()) {  
    9. 上述代碼需要注意的是

       

      • 使用Action Settings.ACTION_MANAGE_OVERLAY_PERMISSION啟動隱式Intent

      • 使用"package:" + getPackageName()攜帶App的包名信息

      • 使用Settings.canDrawOverlays方法判斷授權結果

      請求WRITE_SETTINGS

      [java] copy ?
      1. private   REQUEST_CODE_WRITE_SETTINGS = ;  
      2. private requestWriteSettings() {  
      3.  Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);  
      4.  + getPackageName()));  
      5. @Overrideprotected onActivityResult( requestCode,  resultCode, Intent data) {  
      6. .onActivityResult(requestCode, resultCode, data);  
      7.  (requestCode == REQUEST_CODE_WRITE_SETTINGS) {  
      8.  (Settings.System.canWrite()) {  
      9. 上述代碼需要注意的是

         

        • 使用Action Settings.ACTION_MANAGE_WRITE_SETTINGS 啟動隱式Intent

        • 使用"package:" + getPackageName()攜帶App的包名信息

        • 使用Settings.System.canWrite方法檢測授權結果

        注意:關於這兩個特殊權限,一般不建議應用申請。

         

         

        關於本demo的所有代碼:
        整個申請權限工具類代碼
        package com.example.android.system.runtimepermissions;
         
        import android.Manifest;
        import android.app.Activity;
        import android.content.DialogInterface;
        import android.content.Intent;
        import android.content.pm.PackageManager;
        import android.net.Uri;
        import android.provider.Settings;
        import android.support.annotation.NonNull;
        import android.support.v4.app.ActivityCompat;
        import android.support.v7.app.AlertDialog;
        import android.util.Log;
        import android.widget.Toast;
         
        import java.util.ArrayList;
        import java.util.HashMap;
        import java.util.List;
        import java.util.Map;
         
        /**
        * Created by qianxiaoai on 2016/7/7.
        */
        public class PermissionUtils {
         
        private static final String TAG = PermissionUtils.class.getSimpleName();
        public static final int CODE_RECORD_AUDIO = 0;
        public static final int CODE_GET_ACCOUNTS = 1;
        public static final int CODE_READ_PHONE_STATE = 2;
        public static final int CODE_CALL_PHONE = 3;
        public static final int CODE_CAMERA = 4;
        public static final int CODE_ACCESS_FINE_LOCATION = 5;
        public static final int CODE_ACCESS_COARSE_LOCATION = 6;
        public static final int CODE_READ_EXTERNAL_STORAGE = 7;
        public static final int CODE_WRITE_EXTERNAL_STORAGE = 8;
        public static final int CODE_MULTI_PERMISSION = 100;
         
        public static final String PERMISSION_RECORD_AUDIO = Manifest.permission.RECORD_AUDIO;
        public static final String PERMISSION_GET_ACCOUNTS = Manifest.permission.GET_ACCOUNTS;
        public static final String PERMISSION_READ_PHONE_STATE = Manifest.permission.READ_PHONE_STATE;
        public static final String PERMISSION_CALL_PHONE = Manifest.permission.CALL_PHONE;
        public static final String PERMISSION_CAMERA = Manifest.permission.CAMERA;
        public static final String PERMISSION_ACCESS_FINE_LOCATION = Manifest.permission.ACCESS_FINE_LOCATION;
        public static final String PERMISSION_ACCESS_COARSE_LOCATION = Manifest.permission.ACCESS_COARSE_LOCATION;
        public static final String PERMISSION_READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE;
        public static final String PERMISSION_WRITE_EXTERNAL_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE;
         
        private static final String[] requestPermissions = {
        PERMISSION_RECORD_AUDIO,
        PERMISSION_GET_ACCOUNTS,
        PERMISSION_READ_PHONE_STATE,
        PERMISSION_CALL_PHONE,
        PERMISSION_CAMERA,
        PERMISSION_ACCESS_FINE_LOCATION,
        PERMISSION_ACCESS_COARSE_LOCATION,
        PERMISSION_READ_EXTERNAL_STORAGE,
        PERMISSION_WRITE_EXTERNAL_STORAGE
        };
         
        interface PermissionGrant {
        void onPermissionGranted(int requestCode);
        }
         
        /**
        * Requests permission.
        *
        * @param activity
        * @param requestCode request code, e.g. if you need request CAMERA permission,parameters is PermissionUtils.CODE_CAMERA
        */
        public static void requestPermission(final Activity activity, final int requestCode, PermissionGrant permissionGrant) {
        if (activity == null) {
        return;
        }
         
        Log.i(TAG, "requestPermission requestCode:" + requestCode);
        if (requestCode < 0 || requestCode >= requestPermissions.length) {
        Log.w(TAG, "requestPermission illegal requestCode:" + requestCode);
        return;
        }
         
        final String requestPermission = requestPermissions[requestCode];
         
        //如果是6.0以下的手機,ActivityCompat.checkSelfPermission()會始終等於PERMISSION_GRANTED,
        // 但是,如果用戶關閉了你申請的權限,ActivityCompat.checkSelfPermission(),會導致程序崩潰(java.lang.RuntimeException: Unknown exception code: 1 msg null),
        // 你可以使用try{}catch(){},處理異常,也可以在這個地方,低於23就什么都不做,
        // 個人建議try{}catch(){}單獨處理,提示用戶開啟權限。
        // if (Build.VERSION.SDK_INT < 23) {
        // return;
        // }
         
        int checkSelfPermission;
        try {
        checkSelfPermission = ActivityCompat.checkSelfPermission(activity, requestPermission);
        } catch (RuntimeException e) {
        Toast.makeText(activity, "please open this permission", Toast.LENGTH_SHORT)
        .show();
        Log.e(TAG, "RuntimeException:" + e.getMessage());
        return;
        }
         
        if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) {
        Log.i(TAG, "ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED");
         
         
        if (ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission)) {
        Log.i(TAG, "requestPermission shouldShowRequestPermissionRationale");
        shouldShowRationale(activity, requestCode, requestPermission);
         
        } else {
        Log.d(TAG, "requestCameraPermission else");
        ActivityCompat.requestPermissions(activity, new String[]{requestPermission}, requestCode);
        }
         
        } else {
        Log.d(TAG, "ActivityCompat.checkSelfPermission ==== PackageManager.PERMISSION_GRANTED");
        Toast.makeText(activity, "opened:" + requestPermissions[requestCode], Toast.LENGTH_SHORT).show();
        permissionGrant.onPermissionGranted(requestCode);
        }
        }
         
        private static void requestMultiResult(Activity activity, String[] permissions, int[] grantResults, PermissionGrant permissionGrant) {
         
        if (activity == null) {
        return;
        }
         
        //TODO
        Log.d(TAG, "onRequestPermissionsResult permissions length:" + permissions.length);
        Map<String, Integer> perms = new HashMap<>();
         
        ArrayList<String> notGranted = new ArrayList<>();
        for (int i = 0; i < permissions.length; i++) {
        Log.d(TAG, "permissions: [i]:" + i + ", permissions[i]" + permissions[i] + ",grantResults[i]:" + grantResults[i]);
        perms.put(permissions[i], grantResults[i]);
        if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
        notGranted.add(permissions[i]);
        }
        }
         
        if (notGranted.size() == 0) {
        Toast.makeText(activity, "all permission success" + notGranted, Toast.LENGTH_SHORT)
        .show();
        permissionGrant.onPermissionGranted(CODE_MULTI_PERMISSION);
        } else {
        openSettingActivity(activity, "those permission need granted!");
        }
         
        }
         
         
        /**
        * 一次申請多個權限
        */
        public static void requestMultiPermissions(final Activity activity, PermissionGrant grant) {
         
        final List<String> permissionsList = getNoGrantedPermission(activity, false);
        final List<String> shouldRationalePermissionsList = getNoGrantedPermission(activity, true);
         
        //TODO checkSelfPermission
        if (permissionsList == null || shouldRationalePermissionsList == null) {
        return;
        }
        Log.d(TAG, "requestMultiPermissions permissionsList:" + permissionsList.size() + ",shouldRationalePermissionsList:" + shouldRationalePermissionsList.size());
         
        if (permissionsList.size() > 0) {
        ActivityCompat.requestPermissions(activity, permissionsList.toArray(new String[permissionsList.size()]),
        CODE_MULTI_PERMISSION);
        Log.d(TAG, "showMessageOKCancel requestPermissions");
         
        } else if (shouldRationalePermissionsList.size() > 0) {
        showMessageOKCancel(activity, "should open those permission",
        new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
        ActivityCompat.requestPermissions(activity, shouldRationalePermissionsList.toArray(new String[shouldRationalePermissionsList.size()]),
        CODE_MULTI_PERMISSION);
        Log.d(TAG, "showMessageOKCancel requestPermissions");
        }
        });
        } else {
        grant.onPermissionGranted(CODE_MULTI_PERMISSION);
        }
         
        }
         
         
        private static void shouldShowRationale(final Activity activity, final int requestCode, final String requestPermission) {
        //TODO
        String[] permissionsHint = activity.getResources().getStringArray(R.array.permissions);
        showMessageOKCancel(activity, "Rationale: " + permissionsHint[requestCode], new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
        ActivityCompat.requestPermissions(activity,
        new String[]{requestPermission},
        requestCode);
        Log.d(TAG, "showMessageOKCancel requestPermissions:" + requestPermission);
        }
        });
        }
         
        private static void showMessageOKCancel(final Activity context, String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(context)
        .setMessage(message)
        .setPositiveButton("OK", okListener)
        .setNegativeButton("Cancel", null)
        .create()
        .show();
         
        }
         
        /**
        * @param activity
        * @param requestCode Need consistent with requestPermission
        * @param permissions
        * @param grantResults
        */
        public static void requestPermissionsResult(final Activity activity, final int requestCode, @NonNull String[] permissions,
        @NonNull int[] grantResults, PermissionGrant permissionGrant) {
         
        if (activity == null) {
        return;
        }
        Log.d(TAG, "requestPermissionsResult requestCode:" + requestCode);
         
        if (requestCode == CODE_MULTI_PERMISSION) {
        requestMultiResult(activity, permissions, grantResults, permissionGrant);
        return;
        }
         
        if (requestCode < 0 || requestCode >= requestPermissions.length) {
        Log.w(TAG, "requestPermissionsResult illegal requestCode:" + requestCode);
        Toast.makeText(activity, "illegal requestCode:" + requestCode, Toast.LENGTH_SHORT).show();
        return;
        }
         
        Log.i(TAG, "onRequestPermissionsResult requestCode:" + requestCode + ",permissions:" + permissions.toString()
        + ",grantResults:" + grantResults.toString() + ",length:" + grantResults.length);
         
        if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        Log.i(TAG, "onRequestPermissionsResult PERMISSION_GRANTED");
        //TODO success, do something, can use callback
        permissionGrant.onPermissionGranted(requestCode);
         
        } else {
        //TODO hint user this permission function
        Log.i(TAG, "onRequestPermissionsResult PERMISSION NOT GRANTED");
        //TODO
        String[] permissionsHint = activity.getResources().getStringArray(R.array.permissions);
        openSettingActivity(activity, "Result" + permissionsHint[requestCode]);
        }
         
        }
         
        private static void openSettingActivity(final Activity activity, String message) {
         
        showMessageOKCancel(activity, message, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        Log.d(TAG, "getPackageName(): " + activity.getPackageName());
        Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
        intent.setData(uri);
        activity.startActivity(intent);
        }
        });
        }
         
         
        /**
        * @param activity
        * @param isShouldRationale true: return no granted and shouldShowRequestPermissionRationale permissions, false:return no granted and !shouldShowRequestPermissionRationale
        * @return
        */
        public static ArrayList<String> getNoGrantedPermission(Activity activity, boolean isShouldRationale) {
         
        ArrayList<String> permissions = new ArrayList<>();
         
        for (int i = 0; i < requestPermissions.length; i++) {
        String requestPermission = requestPermissions[i];
         
         
        //TODO checkSelfPermission
        int checkSelfPermission = -1;
        try {
        checkSelfPermission = ActivityCompat.checkSelfPermission(activity, requestPermission);
        } catch (RuntimeException e) {
        Toast.makeText(activity, "please open those permission", Toast.LENGTH_SHORT)
        .show();
        Log.e(TAG, "RuntimeException:" + e.getMessage());
        return null;
        }
         
        if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) {
        Log.i(TAG, "getNoGrantedPermission ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED:" + requestPermission);
         
        if (ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission)) {
        Log.d(TAG, "shouldShowRequestPermissionRationale if");
        if (isShouldRationale) {
        permissions.add(requestPermission);
        }
         
        } else {
         
        if (!isShouldRationale) {
        permissions.add(requestPermission);
        }
        Log.d(TAG, "shouldShowRequestPermissionRationale else");
        }
         
        }
        }
         
        return permissions;
        }
         
        }



        界面調用代碼
        package com.example.android.system.runtimepermissions;
         
        import android.os.Bundle;
        import android.support.annotation.NonNull;
        import android.support.v4.app.ActivityCompat;
        import android.support.v4.app.FragmentActivity;
        import android.support.v4.app.FragmentTransaction;
        import android.view.View;
        import android.widget.Toast;
         
        import com.example.android.common.logger.Log;
         
        /**
        * Created by qianxiaoai on 2016/7/8.
        */
        public class PermissionActivity extends FragmentActivity implements ActivityCompat.OnRequestPermissionsResultCallback{
        private static final String TAG = PermissionActivity.class.getSimpleName();
         
        @Override
        public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission);
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        PermissionsFragment fragment = new PermissionsFragment();
        transaction.replace(R.id.content_fragment, fragment);
        transaction.commit();
         
        }
         
        /**
        * Called when the 'show camera' button is clicked.
        * Callback is defined in resource layout definition.
        */
        public void showCamera(View view) {
        Log.i(TAG, "Show camera button pressed. Checking permission.");
        PermissionUtils.requestPermission(this, PermissionUtils.CODE_CAMERA, mPermissionGrant);
        }
         
        public void getAccounts(View view) {
        PermissionUtils.requestPermission(this, PermissionUtils.CODE_GET_ACCOUNTS, mPermissionGrant);
        }
         
        public void callPhone(View view) {
        PermissionUtils.requestPermission(this, PermissionUtils.CODE_CALL_PHONE, mPermissionGrant);
        }
         
        public void readPhoneState(View view) {
        PermissionUtils.requestPermission(this, PermissionUtils.CODE_READ_PHONE_STATE, mPermissionGrant);
        }
         
        public void accessFineLocation(View view) {
        PermissionUtils.requestPermission(this, PermissionUtils.CODE_ACCESS_FINE_LOCATION, mPermissionGrant);
        }
         
        public void accessCoarseLocation(View view) {
        PermissionUtils.requestPermission(this, PermissionUtils.CODE_ACCESS_COARSE_LOCATION, mPermissionGrant);
        }
         
        public void readExternalStorage(View view) {
        PermissionUtils.requestPermission(this, PermissionUtils.CODE_READ_EXTERNAL_STORAGE, mPermissionGrant);
        }
         
        public void writeExternalStorage(View view) {
        PermissionUtils.requestPermission(this, PermissionUtils.CODE_WRITE_EXTERNAL_STORAGE, mPermissionGrant);
        }
         
        public void recordAudio(View view) {
        PermissionUtils.requestPermission(this, PermissionUtils.CODE_RECORD_AUDIO, mPermissionGrant);
        }
         
         
        private PermissionUtils.PermissionGrant mPermissionGrant = new PermissionUtils.PermissionGrant() {
        @Override
        public void onPermissionGranted(int requestCode) {
        switch (requestCode) {
        case PermissionUtils.CODE_RECORD_AUDIO:
        Toast.makeText(PermissionActivity.this, "Result Permission Grant CODE_RECORD_AUDIO", Toast.LENGTH_SHORT).show();
        break;
        case PermissionUtils.CODE_GET_ACCOUNTS:
        Toast.makeText(PermissionActivity.this, "Result Permission Grant CODE_GET_ACCOUNTS", Toast.LENGTH_SHORT).show();
        break;
        case PermissionUtils.CODE_READ_PHONE_STATE:
        Toast.makeText(PermissionActivity.this, "Result Permission Grant CODE_READ_PHONE_STATE", Toast.LENGTH_SHORT).show();
        break;
        case PermissionUtils.CODE_CALL_PHONE:
        Toast.makeText(PermissionActivity.this, "Result Permission Grant CODE_CALL_PHONE", Toast.LENGTH_SHORT).show();
        break;
        case PermissionUtils.CODE_CAMERA:
        Toast.makeText(PermissionActivity.this, "Result Permission Grant CODE_CAMERA", Toast.LENGTH_SHORT).show();
        break;
        case PermissionUtils.CODE_ACCESS_FINE_LOCATION:
        Toast.makeText(PermissionActivity.this, "Result Permission Grant CODE_ACCESS_FINE_LOCATION", Toast.LENGTH_SHORT).show();
        break;
        case PermissionUtils.CODE_ACCESS_COARSE_LOCATION:
        Toast.makeText(PermissionActivity.this, "Result Permission Grant CODE_ACCESS_COARSE_LOCATION", Toast.LENGTH_SHORT).show();
        break;
        case PermissionUtils.CODE_READ_EXTERNAL_STORAGE:
        Toast.makeText(PermissionActivity.this, "Result Permission Grant CODE_READ_EXTERNAL_STORAGE", Toast.LENGTH_SHORT).show();
        break;
        case PermissionUtils.CODE_WRITE_EXTERNAL_STORAGE:
        Toast.makeText(PermissionActivity.this, "Result Permission Grant CODE_WRITE_EXTERNAL_STORAGE", Toast.LENGTH_SHORT).show();
        break;
        default:
        break;
        }
        }
        };
         
        /**
        * Callback received when a permissions request has been completed.
        */
        @Override
        public void onRequestPermissionsResult(final int requestCode, @NonNull String[] permissions,
        @NonNull int[] grantResults) {
        PermissionUtils.requestPermissionsResult(this, requestCode, permissions, grantResults, mPermissionGrant);
        }
        }



        xml布局
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/horizontal_page_margin"
        android:paddingRight="@dimen/horizontal_page_margin"
        android:paddingTop="@dimen/vertical_page_margin"
        android:paddingBottom="@dimen/vertical_page_margin"
        android:orientation="vertical"
        >
         
        <FrameLayout
        android:id="@+id/content_fragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
         
        <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
         
        <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
         
        <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
         
        <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Camera"
        android:id="@+id/button_camera"
        android:onClick="showCamera"/>
         
        <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="RECORD_AUDIO"
        android:onClick="recordAudio"/>
        </LinearLayout>
         
        <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
         
        <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="GET_ACCOUNTS"
        android:onClick="getAccounts"/>
         
        <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CALL_PHONE"
        android:onClick="callPhone"/>
        </LinearLayout>
         
        <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="PERMISSION_READ_PHONE_STATE"
        android:onClick="readPhoneState"/>
         
        <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ACCESS_FINE_LOCATION"
        android:onClick="accessFineLocation"/>
         
        <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ACCESS_COARSE_LOCATION"
        android:onClick="accessCoarseLocation"/>
         
        <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="READ_EXTERNAL_STORAGE"
        android:onClick="readExternalStorage"/>
         
        <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="WRITE_EXTERNAL_STORAGE"
        android:onClick="writeExternalStorage"/>
         
        </LinearLayout>
        </ScrollView>
         
        </LinearLayout>


        清單文件申請的權限
        <uses-permission android:name="android.permission.CAMERA"/>
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
        <uses-permission android:name="android.permission.CALL_PHONE"/>
        <uses-permission android:name="android.permission.SEND_SMS"/>
        <uses-permission android:name="android.permission.READ_SMS"/>
         
        <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
        <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.RECORD_AUDIO"/>


        部分資源文件
        <?xml version="1.0" encoding="utf-8"?>
        <resources>
        <string-array name="permissions">
        <item>@string/permission_recode_audio_hint</item>
        <item>@string/permission_get_accounts_hint</item>
        <item>@string/permission_read_phone_hint</item>
        <item>@string/permission_call_phone_hint</item>
        <item>@string/permission_camera_hint</item>
        <item>@string/permission_access_fine_location_hint</item>
        <item>@string/permission_access_coarse_location_hint</item>
        <item>@string/permission_read_external_hint</item>
        <item>@string/permission_white_external_hint</item>
        </string-array>
        </resources>
         
        <string name="permission_get_accounts_hint">沒有此權限,無法開啟這個功能,請開啟權限。PERMISSION_GET_ACCOUNTS</string>
        <string name="permission_read_phone_hint">沒有此權限,無法開啟這個功能,請開啟權限。PERMISSION_READ_PHONE_STATE</string>
        <string name="permission_call_phone_hint">沒有此權限,無法開啟這個功能,請開啟權限。PERMISSION_CALL_PHONE</string>
        <string name="permission_camera_hint">沒有此權限,無法開啟這個功能,請開啟權限。PERMISSION_CAMERA</string>
        <string name="permission_access_fine_location_hint">沒有此權限,無法開啟這個功能,請開啟權限。PERMISSION_ACCESS_FINE_LOCATION</string>
        <string name="permission_access_coarse_location_hint">沒有此權限,無法開啟這個功能,請開啟權限。PERMISSION_ACCESS_COARSE_LOCATION</string>
        <string name="permission_read_external_hint">沒有此權限,無法開啟這個功能,請開啟權限。PERMISSION_READ_EXTERNAL_STORAGE</string>
        <string name="permission_white_external_hint">沒有此權限,無法開啟這個功能,請開啟權限。PERMISSION_WRITE_EXTERNAL_STORAGE</string>
        <string name="permission_recode_audio_hint">沒有此權限,無法開啟這個功能,請開啟權限。PERMISSION_RE
         
         

關於自定義權限申請彈框 避免用戶不再申請的問題

Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用戶體驗, 同時也為程序員帶來新的負擔. 動態權限管理就是這樣, 一方面讓用戶更加容易的控制自己的隱私, 一方面需要重新適配應用權限. 時代總是不斷發展, 程序總是以人為本, 讓我們為應用添加動態權限管理吧! 這里提供了一個非常不錯的解決方案, 提供源碼, 項目可以直接使用.

權限

Android系統包含默認的授權提示框, 但是我們仍需要設置自己的頁面. 原因是系統提供的授權框, 會有不再提示的選項. 如果用戶選擇, 則無法觸發授權提示. 使用自定義的提示頁面, 可以給予用戶手動修改授權的指導.

本文示例GitHub下載地址.

在Api 23中, 權限需要動態獲取, 核心權限必須滿足. 標准流程:

流程圖

如果用戶點擊, 不再提示, 則系統授權彈窗將不會彈出. 流程變為:

流程圖

流程就這些, 讓我們看看代碼吧.


1. 權限

在AndroidManifest中, 添加兩個權限, 錄音和修改音量.

<!--危險權限--><uses-permission android:name="android.permission.RECORD_AUDIO"/><!--一般權限--><uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

危險權限必須要授權, 一般權限不需要.

檢測權限類

/**
* 檢查權限的工具類
* <p/>
* Created by wangchenlong on 16/1/26.
*/public class PermissionsChecker {private final Context mContext;
 
public PermissionsChecker(Context context) {
mContext = context.getApplicationContext();
}
 
// 判斷權限集合public boolean lacksPermissions(String... permissions) {
for (String permission : permissions) {
if (lacksPermission(permission)) {
return true;
}
}
return false;
}
 
// 判斷是否缺少權限private boolean lacksPermission(String permission) {
return ContextCompat.checkSelfPermission(mContext, permission) ==
PackageManager.PERMISSION_DENIED;
}
}

2. 首頁

假設首頁需要使用權限, 在頁面顯示前, 即onResume時, 檢測權限, 
如果缺少, 則進入權限獲取頁面; 接收返回值, 拒絕權限時, 直接關閉.

public class MainActivity extends AppCompatActivity {private static final int REQUEST_CODE = 0; // 請求碼// 所需的全部權限static final String[] PERMISSIONS = new String[]{
Manifest.permission.RECORD_AUDIO,
Manifest.permission.MODIFY_AUDIO_SETTINGS
};
 
@Bind(R.id.main_t_toolbar) Toolbar mTToolbar;
 
private PermissionsChecker mPermissionsChecker; // 權限檢測器@Overrideprotected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
 
setSupportActionBar(mTToolbar);
 
mPermissionsChecker = new PermissionsChecker(this);
}
 
@Override protected void onResume() {
super.onResume();
 
// 缺少權限時, 進入權限配置頁面if (mPermissionsChecker.lacksPermissions(PERMISSIONS)) {
startPermissionsActivity();
}
}
 
private void startPermissionsActivity() {
PermissionsActivity.startActivityForResult(this, REQUEST_CODE, PERMISSIONS);
}
 
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 拒絕時, 關閉頁面, 缺少主要權限, 無法運行if (requestCode == REQUEST_CODE && resultCode == PermissionsActivity.PERMISSIONS_DENIED) {
finish();
}
}
}
 

核心權限必須滿足, 如攝像應用, 攝像頭權限就是必須的, 如果用戶不予授權, 則直接關閉.


3. 授權頁

授權頁, 首先使用系統默認的授權頁, 當用戶拒絕時, 指導用戶手動設置, 當用戶再次操作失敗后, 返回繼續提示. 用戶手動退出授權頁時, 給使用頁發送授權失敗的通知.

注意isRequireCheck參數的使用, 防止和系統提示框重疊. 
系統授權提示: ActivityCompat.requestPermissions, ActivityCompat兼容低版本.

效果

自定義授權


關鍵部分就這些了, 動態權限授權雖然給程序員帶來了一些麻煩, 但是對用戶還是很有必要的, 我們也應該歡迎, 畢竟每個程序員都是半個產品經理.


免責聲明!

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



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