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

效果

自定义授权


关键部分就这些了, 动态权限授权虽然给程序员带来了一些麻烦, 但是对用户还是很有必要的, 我们也应该欢迎, 毕竟每个程序员都是半个产品经理.

原文地址:https://www.cnblogs.com/Free-Thinker/p/6047968.html