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

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


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

具體的權限分組狀況以下表:git

 
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部。github

    • (1)檢查是否由此權限checkSelfPermission(),若是已經開啓,則直接作你想作的。網絡

    • (2)若是未開啓,則判斷是否須要向用戶解釋爲什麼申請權限shouldShowRequestPermissionRationale。app

    • (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)每一個應用都有本身的權限管理界面,裏面有本應用申請的權限以及各類狀態,即便用戶已經贊成了你申請的權限,他也隨時能夠關閉
 
 

注意事項ide

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兼容低版本.

效果

自定義受權


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

相關文章
相關標籤/搜索