概述
在安卓6.0以前,安卓应用的权限会在安装阶段向用户展示。但是在使用应用的阶段不需要再申请任何权限。
而在6.0以后,普通股权限只需要在清单文件里注册即可,但是危险权限,则除了注册在清单里之外,还需要在使用app时动态申请。
权限分类
普通权限
在程序运行时自动获取,只需要在清单文件里声明即可。比如 internet。
危险权限
App在一些操作中需要访问用户的隐私信息,比如图库,通讯录。此时安卓系统就会向用户展示所需的权限。
完整的权限申请流程
图示

解释说明
- 
- 判断sdk版本是否低于23,如果是,则不需要申请权限,流程结束。否则往下。
 
 - 
- 调用
checkPermission判断 权限是否已申请成功,如果返回true,流程结束。否则往下。 
 - 调用
 - 
- 调用
requestPermission向系统主动发送申请权限的操作。 
 - 调用
 
但是在 requestPermission 之前,有一个 shouldShowRequestPermissionRationale 的判断
关键的 shouldShowRequestPermissionRationale
- 此判断如果返回了 
true,则说明,之前app已经尝试申请过权限,但是用户点击了拒绝,但是并没有选择Never ask again(以后不再提示). 
这表示,用户没有永久拒绝权限,app还能再次调用 requestPermission尝试申请。
- 如果返回了 
false, 有两种情况:- app从来没有申请过此权限
- 此时,只需要 再次调用 
requestPermission尝试申请 
 - 此时,只需要 再次调用 
 - app申请过此权限,但是被用户拒绝,并且勾选了 
Never ask again(永久拒绝)- 此时,只能弹出自定义对话框,提示用户此操作必须具备权限之后才能继续,并且在点击某个按钮之后进入到 app的权限管理页面。
 
 
 - app从来没有申请过此权限
 
特别注意 ,以上返回true的情况,在某些国产手机上被屏蔽,比如华为,小米,也就是说,这些手机上动态申请权限,只有两种情况,允许 ,或者永久拒绝, 不存在介于两者中间的 暂时拒绝的选项。
标准代码示范
通常,我们需要按照 activity.requestPermissions() 传入 permissions权限数组,以及 requestCode回调码,然后在Activity内进行回调处理。
但是现在有了更优雅的方式,支持我们在 任意代码的位置,不限定activity内,支持fragment,service,甚至一个普通类内进行处理。
基本思想为: 开启一个透明不可见的Activity作为申请权限的载体,申请的动作,以及 申请后的回调 全部放在此Activity内。主要代码如下:
权限申请结果接口
            
            
              java
              
              
            
          
          /**
 * 权限申请结果接口
 */
public interface IPermissionCallback {
    /**
     * 授予权限
     */
    void granted(int requestCode);
    /**
     * 这次拒绝,但是并没有勾选"以后不再提示"
     */
    void denied(int requestCode);
    /**
     * 勾选"以后不再提示",并且拒绝
     */
    void deniedForever(int requestCode);
}
        权限基本功能类
            
            
              java
              
              
            
          
          public class PermissionUtil {
    /**
     * 判断所有权限是否都同意了,都同意返回true 否则返回false
     *
     * @param context     context
     * @param permissions permission list
     * @return return true if all permissions granted else false
     */
    public static boolean hasSelfPermissions(Context context, String... permissions) {
        for (String permission : permissions) {
            if (!hasSelfPermission(context, permission)) {
                return false;
            }
        }
        return true;
    }
    /**
     * 判断单个权限是否同意
     *
     * @param context    context
     * @param permission permission
     * @return return true if permission granted
     */
    private static boolean hasSelfPermission(Context context, String permission) {
        return ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
    }
    /**
     * 检查是否都赋予权限
     *
     * @param grantResults grantResults
     * @return 所有都同意返回true 否则返回false
     */
    public static boolean verifyPermissions(int... grantResults) {
        if (grantResults.length == 0) return false;
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }
    /**
     * 检查所给权限List是否需要给提示
     *
     * @param activity    Activity
     * @param permissions 权限list
     * @return 如果某个权限需要提示则返回true
     */
    public static boolean shouldShowRequestPermissionRationale(Activity activity, String... permissions) {
        for (String permission : permissions) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
                return true;
            }
        }
        return false;
    }
}
        权限申请核心类,唯一入口
            
            
              java
              
              
            
          
          import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import org.jetbrains.annotations.Nullable;
public class PermissionAspectActivity extends Activity {
    private final static String permissionsTag = "permissions";
    private final static String requestCodeTag = "requestCode";
    private static IPermissionCallback mCallback;
    /**
     * 启动当前这个Activity
     */
    public static void startActivity(Context context, String[] permissions, int requestCode, IPermissionCallback callback) {
        Log.d("PermissionAspectTag", "context is : " + context.getClass().getSimpleName());
        if (context == null) return;
        mCallback = callback;
        //启动当前这个Activity并且取消切换动画
        Intent intent = new Intent(context, PermissionAspectActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);//开启新的任务栈并且清除栈顶...为何要清除栈顶
        intent.putExtra(permissionsTag, permissions);
        intent.putExtra(requestCodeTag, requestCode);
        context.startActivity(intent);//利用context启动activity
        if (context instanceof Activity) {//并且,如果是activity启动的,那么还要屏蔽掉activity切换动画
            ((Activity) context).overridePendingTransition(0, 0);
        }
    }
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        String[] permissions = intent.getStringArrayExtra(permissionsTag);
        int requestCode = intent.getIntExtra(requestCodeTag, 0);
        if (PermissionUtil.hasSelfPermissions(this, permissions)) {
            mCallback.granted(requestCode);
            finish();
            overridePendingTransition(0, 0);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(permissions, requestCode);
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        // 现在拿到了权限的申请结果,那么如何处理,我这个Activity只是为了申请,然后把结果告诉外界,所以结果的处理只能是外界传进来
        boolean granted = PermissionUtil.verifyPermissions(grantResults);
        if (granted) {//如果用户给了权限
            mCallback.granted(requestCode);
        } else {
            if (PermissionUtil.shouldShowRequestPermissionRationale(this, permissions)) { // 判断是否还能继续申请
                mCallback.denied(requestCode);
            } else { // 不能再次申请,用户已经完全拒绝
                mCallback.deniedForever(requestCode);
            }
        }
        this.finish();
        overridePendingTransition(0, 0);
    }
}
        注意,此 PermissionAspectActivity 必须在清单文件中注册

而且它的风格是透明的:

效果如下,任意类中都能使用权限申请:
