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);
}
}
 
备注!!!
(1)checkSelfPermission:检查是否拥有这个权限
(2)requestPermissions:请求权限,一般会弹出一个系统对话框,询问用户是否开启这个权限。
(3)shouldShowRequestPermissionRationale:Android原生系统中,如果第二次弹出权限申请的对话框,会出现“以后不再弹出”的提示框,如果用户勾选了,你再申请权限, 则shouldShowRequestPermissionRationale返回true,意思是说要给用户一个 解释,告诉用户为什么要这个权限。然而,在实际开发中,需要注意的是,很多手机对原生 系统做了修改,比如小米,小米4的6.0的shouldShowRequestPermissionRationale 就一直返回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兼容低版本.

效果


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

Android6.0动态申请权限那些坑--以及避免用户选择不再提示后无法获取权限的问题的更多相关文章

  1. 说说Android6.0动态申请权限的那些坑

    白天在做SDK23版本的适配,遇到了不少坑,现在抽空记下来,以此为戒. 首先要知道哪些坑,就得先了解一些定义和基本使用方式. 那么先介绍一下动态申请的权限分组情况. 下面的权限组是由谷歌官方定义的,目 ...

  2. Android6.0动态申请权限

    先直接看代码: public void onClick(View v){ onCallPermission(); } public void onCallPermission(){ if (Build ...

  3. Android6.0动态权限申请步骤以及需要注意的一些坑

    因为工作需要,简单研究了一下Android6.0权限申请,在Google提供的sample的基础上,写了一个简单的demo.算是自己的笔记吧,可能会比较混乱,主要是方便以后查看.后期有别的问题,随时更 ...

  4. Android6.0动态权限申请

    goggle在Android6.0要求部分权限需要动态申请,直接下载AndroidManifest.xml中无效 6.0权限的基本知识,以下是需要单独申请的权限,共分为9组, 每组只要有一个权限申请成 ...

  5. Android6.0动态获取权限

    Android6.0采用新的权限模型,只有在需要权限的时候,才告知用户是否授权,是在runtime时候授权,而不是在原来安装的时候 ,同时默认情况下每次在运行时打开页面时候,需要先检查是否有所需要的权 ...

  6. android6.0动态权限处理<一>

    android 6.0以上为了保护用户的隐私,和以往被人诟病的权限机制,确立了新的权限机制.从 Android 6.0(API 级别 23)开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授 ...

  7. Android 6.0 动态申请 音频+拍照+相册 权限

    1.音频的权限(包括录音和播放) 1.1.首先要在清单中加上两个权限 <uses-permission android:name="android.permission.WRITE_E ...

  8. Retrofit2.0动态url遇到的坑

    1.今天在升级基于RxJava2+Retrofit+RxCache的网络请求封装这套框架的过程中遇到一个问题,当我使用Post动态传入url时,服务器一直返回http404 ,我的请求地址末端是这样的 ...

  9. android6.0以上权限动态申请,有视频链接可以看效果。

    android6.0以上某些权限需要动态申请,虽然现在大多的手机系统版本在6.0,但是升级到6.0及以上是迟早的事,所以如何能够更好的控制动态申请权限时能有好的提示用户,及给用户带去更好的体验,是需要 ...

随机推荐

  1. A glance on VDBI

    Just like other thing in data transfter, a resource should have themselves description. And the reso ...

  2. R语言-回归

    定义: 回归是统计学的核心,它其实是一个广义的概念,通常指那些用一个或多个预测变量来预测响应变量.既:从一堆数据中获取最优模型参数 1.线性回归 1.1简单线性回归 案例:女性预测身高和体重的关系 结 ...

  3. Codeforces Round #445 Div. 1 C Maximum Element (dp + 组合数学)

    题目链接: http://codeforces.com/contest/889/problem/C 题意: 给你 \(n\)和 \(k\). 让你找一种全排列长度为\(n\)的 \(p\),满足存在下 ...

  4. Java 学习(18):Java 序列化& 网络编程& 发送邮件

    --Java 序列化 -- 网络编程 -- 发送邮件 Java 序列化 Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据.有关对象的类型的信 ...

  5. stm32的串口中断

    下面有很多问题没有验证: 在设置USART_CR1中的TE位时,会发送一个空闲帧作为第一次数据发送, 目前我所了解的串口中断发送,有两种方式: 一个是:TC 一个是:TXE 这是判断两个标志位, 第一 ...

  6. element ui源码解析 -- button篇

    要看源码就得从最简单的开始,button够简单的了,就从他开始吧. 安装依赖后源码目录在:node_modules/element-ui/packages中,可以看到这里的文件夹命名是不是很熟悉,就是 ...

  7. 10.13 android输入系统_多点触摸驱动理论与框架

    1.多点触摸驱动理论 驱动程序仅上报多个触点的位置就可以,是放大还是缩小由应用程序控制 对于多点触摸驱动在linux系统中有个输入子系统,其已经实现了open/read/write等接口 我们只需要实 ...

  8. iOS数据存储简要笔记

    1.  数据存储常用的方式 (1)XML 属性列表(plist)归档 (2)preference(偏好设置) (3)NSKeyedArchiver归档(NSCoding) (4)  SQLite3   ...

  9. PHP解决约瑟夫环问题

    PHP解决约瑟夫环问题 一.总结 二.PHP解决约瑟夫环问题 约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到 ...

  10. stm32的timer