精华内容
下载资源
问答
  • Android开发之 permission动态权限获取

    万次阅读 多人点赞 2019-08-19 10:57:20
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> ...

    权限查看:戳这里

    在文章最后附上DEMO

    DEMO效果:

     

    一、说明:

    1、支持单个权限、多个权限、单个权限组、多个权限组请求

    2、不指定权限则自动获取清单文件上的危险权限进行申请

    3、如果动态申请的权限没有在清单文件中注册会抛出异常

    4、支持大部分国产手机直接跳转到具体的权限设置页面

    5、可设置被拒绝后继续申请,直到用户授权或者永久拒绝

    6、支持请求6.0及以上的悬浮窗权限和8.0及以上的安装权限

    7、Android 6.0 (API 23) 之前应用的权限在安装时全部授予,运行时应用不再需要询问用户。在 Android 6.0 或更高版本对权限进行了分类,对某些涉及到用户隐私的权限可在运行时根据用户的需要动态授予。这样就不需要在安装时被强迫同意某些权限。

    二、权限

    1、Android系统对所有的危险权限进行了分组,称为 权限组 。属于同一组的危险权限将自动合并授予,用户授予应用某个权限组的权限,则应用将获得该权限组下的所有权限(前提是相关权限在 AndroidManifest.xml 中有声明)。

    危险权限 和 权限组 列表如下: 在 AndroidManifest.xml 声明过的危险权限对应的权限组可以在系统 “设置” -> “应用” -> “应用信息” -> “权限” 中查看,可以手动授权和取消授权。

    三、在运行时请求权限

    1、设备系统是 Android 6.0 (API 23) 或更高版本,并且应用的 targetSdkVersion 是 23 或更高版本,则针对在 AndroidManifest.xml 中声明的危险权限,在运行时还需要动态请求用户授权。

    2、动态权限请求相关操作的API封装在在android.support.v4包中,发起请求权限的Activity需要直接或间接继承android.support.v4.app.FragmentActivity

    3、 也可以在直接或间接继承 android.support.v4.app.Fragment 的 Fragment 中发起权限请求。

    四、代码步骤中主要包含以下几个方法:

    1、检查权限

    // 检查权限
    
    ContextCompat.checkSelfPermission(Context context, String permission)

          返回值(android.content.pm.PackageManager中的常量):

    有权限: PackageManager.PERMISSION_GRANTED
    无权限: PackageManager.PERMISSION_DENIED

    当应用需要用到危险权限时,在执行权限相关代码前,使用该方法判断是否拥有指定的权限。有权限,则继续执行设计需要权限的代码;无权限,则向用户请求授予权限。

    2、解释权限

    // 解释权限
    ActivityCompat.shouldShowRequestPermissionRationale(Activity activity, String permission)

    2.1判断是否有必要向用户解释为什么要这项权限。如果应用第一次请求过此权限,但是被用户拒绝了,则之后调用该方法将返回 true,此时就有必要向用户详细说明需要此权限的原因(个人认为此方法是可选的)。

     2.2如果应用第一次请求此权限时被用户拒绝,第二次再请求此权限时,用户勾选了权限请求对话框的“不再询问”,则此方法返回 false。如果设备规范禁止应用拥有该权限,此方法也返回 false。

    3、请求权限

    // 请求权限
    ActivityCompat.requestPermissions(Activity activity, String[] permissions, int requestCode)
    
    1. 权限参数传入的是数组,可以调用该方法一次请求多个权限;
    2. 传入的权限数组参数以单个具体权限为单位,但弹框询问用户授权时,属于同一权限组的权限将自动合并询问授权一次;
    3. 请求的权限必须事先在 AndroidManifest.xml 中有声明,否则调用此方法请求时,将不弹框,而是直接返回“拒绝”的结果;
    4. 第一次请求权限时,用户点击了“拒绝”,第二次再请求该权限时,对话框将出现“不再询问”复选框,如果用户勾选了“不再询问”并点击了“拒绝”,则之后再请求此权限组时将不弹框,而是直接返回“拒绝”的结果。

    4、处理结果

    请求权限的结果返回和接收一个Activity的返回类似,重写 FragmentActivity 或 (v4) Fragment 中的 onRequestPermissionsResult(...) 方法。

    /**
     * 处理权限请求结果
     *
     * @param requestCode
     *          请求权限时传入的请求码,用于区别是哪一次请求的
     *
     * @param permissions
     *          所请求的所有权限的数组
     *
     * @param grantResults
     *          权限授予结果,和 permissions 数组参数中的权限一一对应,元素值为两种情况,如下:
     *          授予: PackageManager.PERMISSION_GRANTED
     *          拒绝: PackageManager.PERMISSION_DENIED
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        // ...
    }

     

    五、 代码示例

    DEMO功能很简单,如下:

    1. 点击一个按钮,如果有相应权限就进行备份通讯录操作;
    2. 如果没有相应的权限,则向用户申请权限;
    3. 如果用户授权通过,则继续进行备份通讯录操作;
    4. 如果用户拒绝授权,则弹出对话框引导用户跳转到应用权限管理界面手动授权。

    部分代码示例:

    1、首先在 AndroidManifest.xml 中声明权限

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.xiets.demoapp">
    
        <!-- 声明所有需要的权限(包括普通权限和危险权限) -->
        <uses-permission android:name="android.permission.READ_CONTACTS"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    
        <application
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme">
    
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
        </application>
    
    </manifest>

    2、布局文件 activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp">
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="click"
            android:textSize="20sp"
            android:text="备份通讯录" />
    
    </RelativeLayout>

    3、MainActivity

    package com.xiets.demoapp;
    
    import android.Manifest;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.net.Uri;
    import android.os.Bundle;
    import android.provider.Settings;
    import android.support.annotation.NonNull;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.Toast;
    
    /**
     * 一键备份通讯录
     *
     * @author xietansheng
     */
    public class MainActivity extends AppCompatActivity {
    
        private static final int MY_PERMISSION_REQUEST_CODE = 10000;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        /**
         * 点击按钮,将通讯录备份保存到外部存储器备。
         *
         * 需要3个权限(都是危险权限):
         *      1. 读取通讯录权限;
         *      2. 读取外部存储器权限;
         *      3. 写入外部存储器权限.
         */
        public void click(View view) {
            /**
             * 第 1 步: 检查是否有相应的权限,根据自己需求,进行添加相应的权限
             */
            boolean isAllGranted = checkPermissionAllGranted(
                    new String[] {
                            Manifest.permission.READ_CONTACTS,
                            Manifest.permission.READ_EXTERNAL_STORAGE,
                            Manifest.permission.WRITE_EXTERNAL_STORAGE
                    }
            );
            // 如果这3个权限全都拥有, 则直接执行备份代码
            if (isAllGranted) {
                doBackup();
                return;
            }
    
            /**
             * 第 2 步: 请求权限
             */
            // 一次请求多个权限, 如果其他有权限是已经授予的将会自动忽略掉
            ActivityCompat.requestPermissions(
                    this,
                    new String[] {
                            Manifest.permission.READ_CONTACTS,
                            Manifest.permission.READ_EXTERNAL_STORAGE,
                            Manifest.permission.WRITE_EXTERNAL_STORAGE
                    },
                    MY_PERMISSION_REQUEST_CODE
            );
        }
    
        /**
         * 检查是否拥有指定的所有权限
         */
        private boolean checkPermissionAllGranted(String[] permissions) {
            for (String permission : permissions) {
                if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                    // 只要有一个权限没有被授予, 则直接返回 false
                    return false;
                }
            }
            return true;
        }
    
        /**
         * 第 3 步: 申请权限结果返回处理
         */
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
            if (requestCode == MY_PERMISSION_REQUEST_CODE) {
                boolean isAllGranted = true;
    
                // 判断是否所有的权限都已经授予了
                for (int grant : grantResults) {
                    if (grant != PackageManager.PERMISSION_GRANTED) {
                        isAllGranted = false;
                        break;
                    }
                }
    
                if (isAllGranted) {
                    // 如果所有的权限都授予了, 则执行备份代码
                    doBackup();
    
                } else {
                    // 弹出对话框告诉用户需要权限的原因, 并引导用户去应用权限管理中手动打开权限按钮
                    openAppDetails();
                }
            }
        }
    
        /**
         * 第 4 步: 备份通讯录操作
         */
        private void doBackup() {
            // 本文主旨是讲解如果动态申请权限, 具体备份代码不再展示, 就假装备份一下
            Toast.makeText(this, "正在备份通讯录...", Toast.LENGTH_SHORT).show();
        }
    
        /**
         * 打开 APP 的详情设置
         */
        private void openAppDetails() {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("备份通讯录需要访问 “通讯录” 和 “外部存储器”,请到 “应用信息 -> 权限” 中授予!");
            builder.setPositiveButton("去手动授权", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent = new Intent();
                    intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                    intent.addCategory(Intent.CATEGORY_DEFAULT);
                    intent.setData(Uri.parse("package:" + getPackageName()));
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
                    intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                    startActivity(intent);
                }
            });
            builder.setNegativeButton("取消", null);
            builder.show();
        }
    
    }

    DEMO地址:下载

    展开全文
  • 安卓Permission权限请求

    万次阅读 2019-01-07 10:52:10
    AndroidPermission,权限自动请求示例(所需权限被关闭时,会自动请求) 权限请求源码:(将应用的Activity设为PermissionActivity的子类,即可自动请求所需权限) package com.sc.demo; import android...

     AndroidPermission,权限自动请求示例(所需权限被关闭时,会自动请求)

    权限请求源码:(将应用的Activity设为PermissionActivity的子类,即可自动请求所需权限)

    package com.sc.demo;
    
    import android.os.Bundle;
    
    import com.sc.permission.PermissionActivity;
    import com.sci.androidpermission.R;
    
    /**  
     * 权限请求示例
     */
    public class MainActivity extends PermissionActivity
    {	
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		this.setContentView(R.layout.activity_main);
    	}
    }
    
    package com.sc.permission;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.widget.Toast;
    
    import com.sc.permission.PermissionTool.PermissionCallBack;
    
    /** 
     * https://blog.csdn.net/scimence/article/details/85989997
     * 继承PermissionActivity可自动进行权限申请,
     * 
     * AndroidBuild Target 设置为6.0,
     * <uses-sdk android:targetSdkVersion="23" /> 
     * 
     * ----- 2019-1-7 上午9:08:39 scimence  */
    public class PermissionActivity extends Activity
    {
    	/** 自动解析并请求AndroidManifest.xml中设置的所有权限 */
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		PermissionTool.Request(this);
    		
    //		PermissionTool.Request(this, new PermissionCallBack()
    //		{
    //			@Override
    //			public void Success()
    //			{
    //				// 在权限请求完成后,才可执行的逻辑
    //				Toast.makeText(PermissionActivity.this, "PermissionCallBack -> Success", Toast.LENGTH_SHORT).show();
    //			}
    //		});
    	}
    	
    	/** 处理权限请求结果,若未授权,则继续请求 */
    	public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
    	{
    		super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    		PermissionTool.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
    	}
    	
    	
    	/** Activity执行结果 */
    	protected void onActivityResult(int requestCode, int resultCode, Intent data)
    	{
    		super.onActivityResult(requestCode, resultCode, data);
    		PermissionTool.onActivityResult(this, requestCode, resultCode, data);
    	}
    }
    
    
    package com.sc.permission;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.content.pm.PermissionInfo;
    import android.net.Uri;
    import android.os.Build;
    import android.os.Handler;
    import android.os.Looper;
    import android.os.Process;
    import android.util.Log;
    
    
    /** 
     * https://blog.csdn.net/scimence/article/details/85989997 
     * permissionTool.java: 安卓权限请求 <uses-sdk android:targetSdkVersion="23" /> 
     * 
     * 用法1: 
     * 1、请求权限 			PermissionTool.Request(activity); 
     * 2、处理权限请求结果 		PermissionTool.onRequestPermissionsResult(activity, requestCode, permissions, grantResults); 
     * 3、系统设置权限执行回调 	PermissionTool.onActivityResult(this, requestCode, resultCode, data); 
     * 
     * 简易用法:继承PermissionActivity 
     * 
     * ----- 2019-1-7 上午9:08:39 scimence */
    public class PermissionTool
    {
    	/** 请求权限 */
    	public static void Request(Activity activity)
    	{
    		String[] permissions = getPermissions(activity);	// 获取应用的所有权限
    		requestPermissionProcess(activity, permissions);	// 执行权限请求逻辑
    	}
    	
    	/** 获取AndroidManifest.xml中所有permission信息, 返回信息如{"android.permission.INTERNET", "android.permission.READ_PHONE_STATE"} */
    	public static String[] getPermissions(Activity activity)
    	{
    		String[] permissions = new String[] {};
    		try
    		{
    			PackageManager packageManager = activity.getPackageManager();
    			String packageName = activity.getPackageName();
    			
    			PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
    			permissions = packageInfo.requestedPermissions;
    		}
    		catch (Exception e)
    		{	
    			
    		}
    		return permissions;
    	}
    	
    	
    	/** 请求所需权限 如: String[] permissions = { Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_EXTERNAL_STORAGE }; */
    	public static void requestPermissionProcess(final Activity activity, final String... permissions)
    	{
    		new Handler(Looper.getMainLooper()).post(new Runnable()
    		{
    			@Override
    			public void run()
    			{
    
    				// 版本判断。当手机系统大于 23 时,才有必要去判断权限是否获取
    				int sdkVersion = activity.getApplicationInfo().targetSdkVersion;
    				if (Build.VERSION.SDK_INT >= 23 && sdkVersion >= 23)
    				{
    					// 检查该权限是否已经获取
    					ArrayList<String> list = new ArrayList<String>();
    					for (String permission : permissions)
    					{
    						try
    						{
    							// int ret = ContextCompat.checkSelfPermission(activity.getApplicationContext(), permission);
    							int ret = activity.checkPermission(permission, Process.myPid(), Process.myUid());
    							
    							// 权限是否已经 授权 GRANTED---授权 DINIED---拒绝
    							if (ret != PackageManager.PERMISSION_GRANTED && !list.contains(permission)) list.add(permission);
    						}
    						catch (Exception ex)
    						{
    							Log.e("permissionTool", "是否已授权,无法判断权限:" + permission);
    						}
    					}
    					
    					// 请求没有的权限
    					if (list.size() > 0)
    					{
    						String[] permission = list.toArray(new String[list.size()]);
    						activity.requestPermissions(permission, PermissionRquestCode);	// 从权限请求返回
    					}
    					else
    					{
    						Log.e("permissionTool", "应用所需权限,均已授权。" );
    						CallBak();
    					}
    					
    				}
    				else
    				{
    					CallBak();
    				}
    			}
    		});
    	}
    	
    	/** Android 6.0以上版本需要请求的权限信息(targetSdkVision >= 23) */
    	private static String[] SettingPermission = new String[] { "android.permission.SEND_SMS", "android.permission.RECEIVE_SMS", "android.permission.READ_SMS",
    			"android.permission.RECEIVE_WAP_PUSH", "android.permission.RECEIVE_MMS", "android.permission.READ_EXTERNAL_STORAGE",
    			"android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS",
    			"android.permission.GET_ACCOUNTS", "android.permission.READ_PHONE_STATE", "android.permission.CALL_PHONE", "android.permission.READ_CALL_LOG",
    			"android.permission.WRITE_CALL_LOG", "android.permission.ADD_VOICEMAIL", "android.permission.USE_SIP", "android.permission.PROCESS_OUTGOING_CALLS",
    			"android.permission.READ_CALENDAR", "android.permission.WRITE_CALENDAR", "android.permission.CAMERA", "android.permission.ACCESS_FINE_LOCATION",
    			"android.permission.ACCESS_COARSE_LOCATION", "android.permission.BODY_SENSORS", "android.permission.RECORD_AUDIO" };
    	private static List<String> permissinList = Arrays.asList(SettingPermission);
    	
    
    	final static int PermissionRquestCode = 6554;
    	
    	/** 处理权限请求结果逻辑,再次调用请求、或提示跳转设置界面 */
    	public static void onRequestPermissionsResult(Activity activity, int requestCode, String[] permissions, int[] grantResults)
    	{
    		if (requestCode == PermissionRquestCode)
    		{
    			ArrayList<String> needPermissions = new ArrayList<String>();	// 应用未授权的权限
    			ArrayList<String> noaskPermissions = new ArrayList<String>();	// 用户默认拒绝的权限
    			
    			for (int i = 0; i < permissions.length; i++)
    			{
    				String permission = permissions[i];
    				if (grantResults[i] != PackageManager.PERMISSION_GRANTED)
    				{
    					try
    					{
    						// 用户点了默认拒绝权限申请,这时候就得打开自定义dialog,让用户去设置里面开启权限
    						if (!activity.shouldShowRequestPermissionRationale(permission))
    						{
    							Log.i("permissionTool", "permissinList Size:" + permissinList.size());
    							if (permissinList.contains(permission))
    							{
    								noaskPermissions.add(permission);
    							}
    							else
    							{
    								Log.i("permissionTool", "自动允许或拒绝权限:" + permission);
    							}
    						}
    						else
    						{
    							// 记录需要请求的权限信息
    							needPermissions.add(permission);
    						}
    					}
    					catch (Exception ex)
    					{
    						Log.e("permissionTool", "自动允许或拒绝权限,无法判断权限:" + permission);
    					}
    				}
    			}
    			
    			if (needPermissions.size() > 0)
    			{
    				requestPermissionProcess(activity, needPermissions.toArray(new String[needPermissions.size()]));	// 请求未授予的权限
    			}
    			else if (noaskPermissions.size() > 0)
    			{
    				PermissionSetting(activity, noaskPermissions.get(0));	// 对话框提示跳转设置界面,添加权限
    			}
    			else
    			{
    				CallBak();
    			}
    		}
    	}
    	
    	/** 在手机设置中打开的应用权限 */
    	private static void PermissionSetting(final Activity activity, final String permission)
    	{
    		if (permission.trim().equals("")) return;
    		
    		// 获取权限对应的标题和详细说明信息
    		String permissionLabel = "";
    		String permissionDescription = "";
    		
    		try
    		{
    			PackageManager packageManager = activity.getPackageManager();
    			// Tools.showText("permission -> " + permission);
    			
    			PermissionInfo permissionInfo = packageManager.getPermissionInfo(permission, 0);
    			
    			// PermissionGroupInfo permissionGroupInfo = packageManager.getPermissionGroupInfo(permissionInfo.group, 0);
    			// Tools.showText("permission组 -> " + permissionGroupInfo.loadLabel(packageManager).toString());
    			
    			permissionLabel = permissionInfo.loadLabel(packageManager).toString();
    			// Tools.showText("permission名称 -> " + permissionLabel);
    			
    			permissionDescription = permissionInfo.loadDescription(packageManager).toString();
    			// Tools.showText("permission描述 -> " + permissionDescription);
    			
    		}
    		catch (Exception ex)
    		{
    			return;
    		}
    		
    		// 自定义Dialog弹窗,显示权限请求
    		permissionLabel = "应用需要权限:" + permissionLabel + "\r\n" + permission;
    		AlertDialog.Builder builder = new AlertDialog.Builder(activity);
    		builder.setCancelable(false);
    		builder.setTitle(permissionLabel);
    		builder.setMessage(permissionDescription);
    		builder.setPositiveButton("去添加 权限", new DialogInterface.OnClickListener()
    		{
    			@Override
    			public void onClick(DialogInterface dialog, int which)
    			{
    				dialog.dismiss();
    				
    				// 打开应用对应的权限设置界面
    				String action = android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS;
    				Intent intent = new Intent(action);
    				Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
    				intent.setData(uri);
    				activity.startActivityForResult(intent, PermissionResultCode);	// 从应用设置界面返回时执行OnActivityResult
    			}
    		});
    		builder.setNegativeButton("拒绝则 退出", new DialogInterface.OnClickListener()
    		{
    			@Override
    			public void onClick(DialogInterface dialog, int which)
    			{
    				dialog.dismiss();
    				
    				// 若拒绝了所需的权限请求,则退出应用
    				activity.finish();
    				System.exit(0);
    			}
    		});
    		builder.show();
    	}
    	
    	final static int PermissionResultCode = 6555;
    	
    	/** Activity执行结果,回调函数 */
    	public static void onActivityResult(final Activity activity, int requestCode, int resultCode, Intent data)
    	{
    		// Toast.makeText(activity, "onActivityResult设置权限!", Toast.LENGTH_SHORT).show();
    		if (requestCode == PermissionResultCode)	// 从应用权限设置界面返回
    		{
    			// Toast.makeText(activity, "onActivityResult -> " + resultCode, Toast.LENGTH_SHORT).show();
    			PermissionTool.Request(activity);		// 再次进行权限请求(若存在未获取到的权限,则会自动申请)
    		}
    	}
    	
    	// private static boolean isCallBack = false;
    	/** 执行权限请求回调逻辑 */
    	private static void CallBak()
    	{
    	// 	if(!isCallBack)
    	// 	{
    	// 		isCallBack = true;
    			if (CallInstance != null) CallInstance.Success();
    	// 	}
    	}
    	
    	// ----------
    	
    	/** 权限请求回调 */
    	public static abstract class PermissionCallBack
    	{
    		/** 权限请求成功 */
    		public abstract void Success();
    	}
    	
    	private static PermissionCallBack CallInstance = null;
    	
    	/** 请求权限, 请求成功后执行回调逻辑 */
    	public static void Request(Activity activity, PermissionCallBack Call)
    	{
    		CallInstance = Call;
    		String[] permissions = getPermissions(activity);	// 获取应用的所有权限
    		
    		new Handler(Looper.getMainLooper()).postDelayed(new Runnable()
    		{
    			@Override
    			public void run()
    			{
    				CallBak();
    			}
    		}, 30 * 1000);	// 30秒后自动执行回调逻辑。确保回调会被调用。
    		
    		requestPermissionProcess(activity, permissions);	// 执行权限请求逻辑
    		
    	}
    }
    

     

    AndroidPermission示例源码下载

    Android6.0模拟器

    展开全文
  • Android Runtime Permission 详解

    千次阅读 2018-05-04 15:31:34
    前言: 在Android 6.0 之前权限管理存在一些弊端: ... 在Android 6.0 之前权限都称为install time permission,应用软件在安装以后用户不能修改permission 的授权情况,也不允许对permission 进行单独...

    前言:

    在Android 6.0 之前权限管理存在一些弊端:

    • 权限系统只会在安装的时候询问一次,用户可以选择性的授予应用相关权限。但是一旦安装了,应用软件会在用户毫不知情的情况下访问权限内的所有东西。
    • 在Android 6.0 之前权限都称为install time permission,应用软件在安装以后用户不能修改permission 的授权情况,也不允许对permission 进行单独的授权和撤销。

    Android 6.0 及更高版本中的 Android 应用权限模式旨在使权限更易于用户理解、更实用、更安全。该模式将需要危险权限的 Android 应用从安装时权限模式转移至运行时权限模式:

    • 安装时权限(Android 5.1 及更低版本,或者应用目标SDK为22或更低版本)。用户在安装或更新应用时,向应用授予危险权限。OEM/运营商可以在不通知用户的情况下,预先安装具有预授权的应用。
    • 运行时权限(Android 6.0 及更高版本,或者应用目标SDK为23或更高版本)。用户在应用运行时向应用授予危险权限。应用决定何时申请权限(例如,在应用启动或用户访问特定功能时申请权限),但必须允许用户授予/拒绝授予应用访问特定权限组的权限。OEM/运营商可以预安装应用,但不得预先授予权限。

     

    Runtime permission 向前兼容

    • 如果是M 之前的应用安装在M 或更高的版本上,permission 会安装之前旧的方式管理,也就是install time permission model。需要注意的是,在M 的系统上用户可以在设置里对permission 进行授权和撤销,应用软件没有相应的权限可能会出现crash。
    • 如果M 之后的应用安装在M 之前的版本上,permission 没有runtime 一说,还是会安装以前的install time permission。

     

    系统权限的使用

    权限的使用:

    基本 Android 应用默认情况下未关联权限,这意味着它无法执行对用户体验或设备上任何数据产生不利影响的任何操作。要利用受保护的设备功能,必须在应用清单(AndroidManifest.xml)中包含一个或多个 <uses-permission> 标记。
    例如,需要监控传入的短信的应用要指定:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.android.app.myapp" >
        <uses-permission android:name="android.permission.RECEIVE_SMS" />
        ...
    </manifest>

     

    权限级别:

    如果您的应用在其清单中列出正常权限(即,不会对用户隐私或设备操作造成很大风险的权限),系统会自动授予这些权限。如果您的应用在其清单中列出危险权限(即,可能影响用户隐私或设备正常操作的权限),系统会要求用户明确授予这些权限。Android 发出请求的方式取决于系统版本,而系统版本是应用的目标:

    • 如果设备运行的是 Android 6.0(API 级别 23)或更高版本,并且应用的 targetSdkVersion 是 23 或更高版本,则应用在运行时向用户请求权限。用户可随时调用权限,因此应用在每次运行时均需检查自身是否具备所需的权限。如需了解有关在应用中请求权限的详细信息,请参阅使用系统权限培训指南。
    • 如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在用户安装应用时要求用户授予权限。如果将新权限添加到更新的应用版本,系统会在用户更新应用时要求授予该权限。用户一旦安装应用,他们撤销权限的唯一方式是卸载应用。

     

    通常,权限失效会导致 SecurityException 被扔回应用。但不能保证每个地方都是这样。例如,sendBroadcast(Intent) 方法在数据传递到每个接收者时会检查权限,在方法调用返回后,即使权限失效,您也不会收到异常。但在几乎所有情况下,权限失效会记入系统日志。
    Android 系统提供的权限请参阅 Manifest.permission。此外,任何应用都可定义并实施自己的权限,因此这不是所有可能权限的详尽列表。
    可能在程序运行期间的多个位置实施特定权限:

    • 在调用系统时,防止应用执行某些功能。
    • 在启动 Activity 时,防止应用启动其他应用的 Activity。
    • 在发送和接收广播时,控制谁可以接收您的广播,谁可以向您发送广播。
    • 在访问和操作内容提供程序时。
    • 绑定至服务或启动服务。

     

    权限自动调整:

    随着时间的推移,平台中可能会加入新的限制,要想使用特定 API,您的应用可能必须请求之前不需要的权限。因为现有应用假设可随意获取这些 API 应用的访问权限,所以 Android 可能会将新的权限请求应用到应用清单,以免在新平台版本上中断应用。Android 将根据 targetSdkVersion 属性提供的值决定应用是否需要权限。如果该值低于在其中添加权限的版本,则 Android 会添加该权限。

    例如,API 级别 4 中加入了 WRITE_EXTERNAL_STORAGE 权限,用以限制访问共享存储空间。如果您的 targetSdkVersion 为 3 或更低版本,则会向更新 Android 版本设备上的应用添加此权限。

    为避免这种情况,并且删除您不需要的默认权限,请始终将 targetSdkVersion 更新至最高版本。可在 Build.VERSION_CODES 文档中查看各版本添加的权限。

     

    权限等级分类:

    这里说的等级分类其实是permission中的属性:

    android:protectionLevel

    被分为几个类别:Normal, Dangerous, Signature, SigatureOrSystem

    • Normal Permission指的是那些 app获取它所在的sandbox(每个进程都有独立的一个沙箱)以外的数据和资源所对应的权限,这些权限一般不会对用户的隐私信息造成风险. 比如,设置时区的权限(SET_TIME_ZONE)。对于此类权限,app申请后系统会自动赋予。
    • Dangerous Permission指的是那些可能对用户的隐私信息造成风险,或者可能影响用户数据的行为权限。比如读取用户的联系人。对于Dangerous Permission,app必须显示的获取用户的允许才可以正常使用。Runtime Permission机制针对的即是此类 dangerous permission。
    • Signature permission:权限请求者只有使用和[权限声明者]相同的证书来签名的情况下,才可以使用的权限。如果证书匹配,系统会自动赋予这些权限,不需要通知或请求用户。
    • SignatureOrSystem: SDK版本23之前叫“signature|privileged”,该类权限除了上述的 Signature Permission以外,还包括那些只赋予Android System Image内的应用的权限。Android并不建议app使用这类,因为Signature Permission已经能满足大部分的需求,不管这些app是否是build在System Image里。

     

    特殊权限:

    上面说明了permission 中属性android:protectionLevel 使用意义,其中存在一些特殊的权限,这些权限比较敏感,在使用的时候必须特殊处理。SYSTEM_ALERT_WINDOWWRITE_SETTINGS就是此类比较敏感的权限。

    例如 WRITE_SETTINGS:

        <permission android:name="android.permission.WRITE_SETTINGS"
            android:label="@string/permlab_writeSettings"
            android:description="@string/permdesc_writeSettings"
            android:protectionLevel="signature|preinstalled|appop|pre23" />

    官方解释如下:

      Note: If the app targets API level 23 or higher, the app user must explicitly grant this
    permission to the app through a permission management screen.
      The app requests the user's approval by sending an intent with action ACTION_MANAGE_WRITE_SETTINGS.
      The app can check whether it has this authorization by calling Settings.System.canWrite().

     

    Dangerous Permission:

    Android 6.0 及更高版本要求危险权限必须使用运行时权限模式。危险权限是具有更高风险的权限(例如READ_CALENDAR),此类权限允许寻求授权的应用访问用户私人数据或获取可对用户造成不利影响的设备控制权。要查看危险权限列表,请运行以下命令:

    adb shell pm list permissions -g -d

    Android 6.0 及更高版本不会更改常规权限的行为(包括常规权限、系统权限和签名权限在内的所有非危险权限)。常规权限是具有较低风险的权限(例如 SET_WALLPAPER),它允许请求授权的应用访问隔离的应用级功能,对其他应用、系统或用户的风险非常小。在 Android 5.1 及更低版本中,系统在安装应用时,自动向请求授权的应用授予常规权限,并且无需提示用户进行批准。

     

    permission group:

    所有危险的 Android 系统权限都属于权限组。如果设备运行的是 Android 6.0(API 级别 23),并且应用的 targetSdkVersion 是 23 或更高版本,则当用户请求危险权限时系统会发生以下行为:

    • 如果应用请求其清单中列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求 READ_CONTACTS 权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。
    • 如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请WRITE_CONTACTS,系统将立即授予该权限

    任何权限都可属于一个权限组,包括正常权限和应用定义的权限。但权限组仅当权限危险时才影响用户体验。可以忽略正常权限的权限组。
    如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在安装时要求用户授予权限。再次强调,系统只告诉用户应用需要的权限组,而不告知具体权限。(注意这一点,在国内CTA要求这些组权限必须要细分)

     

    使用权限

    检查权限情况:

    //检查某个 uid 和 pid是否有permission
    //如果返回PackageManager#PERMISSION_GRANTED,说明该权限已经被allowed
    //如果返回PackageManager#PERMISSION_DENIED,说明该权限不被allowed
    public int checkPermission(String permission, int pid, int uid)
    //同checkPermission,这里提供给当前进程调用
    public int checkCallingPermission(String permission)
    //同checkPermission,这里跟checkCallingPermission区别是,不需要判断调用者是否是当前进程
    public int checkCallingOrSelfPermission(String permission)
    //同checkPermission,主要是当返回不是PackageManager.PERMISSION_GRANTED会抛出SecurityException
    public void enforcePermission(String permission, int pid, int uid, String message)
    //同enforcePermission
    public void enforceCallingPermission(String permission, String message)
    //同enforcePermission
    public void enforceCallingOrSelfPermission(String permission, String message)

    详细的source code 可以看ContextImpl.java

    需要注意的是,在调入PMS之前还有很多路要走:

        public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
            if (permission == null) { //传入的permission 不能为null
                throw new IllegalArgumentException("permission is null");
            }
    
            try {
                return ActivityManager.getService().checkPermissionWithToken(//详细看AMS
                        permission, pid, uid, callerToken);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

     

    申请权限:

        public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
            if (requestCode < 0) {
                throw new IllegalArgumentException("requestCode should be >= 0");
            }
            if (mHasCurrentPermissionsRequest) {
                Log.w(TAG, "Can reqeust only one set of permissions at a time");
                // Dispatch the callback with empty arrays which means a cancellation.
                onRequestPermissionsResult(requestCode, new String[0], new int[0]);
                return;
            }
            Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
            startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
            mHasCurrentPermissionsRequest = true;
        }

    调用该函数,系统会调出一个对话框,提示用户是否要给予相应权限。用户允许或拒绝相应的权限后,app的onRequestPermissionsResult(int, String[], int[])会被调用,告诉app相应的permission是被授权或者拒绝。详细的代码可以看Activity.java,这里需要注意的是:

    1、requestCode 是用来匹配的,但是必须是非负数

    2、buildRequestPermissionsIntent

    注意这里这个函数,这里就是在request permission时候需要弹出对话框的invoke 地方。

        public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
            if (ArrayUtils.isEmpty(permissions)) {
               throw new IllegalArgumentException("permission cannot be null or empty");
            }
            Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS); //注意这里的Action
            intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions); //Extra传的是permission name
            intent.setPackage(getPermissionControllerPackageName());//注意,系统直接规定了packageName
            return intent;
        }

    这里build 出来的intent,系统会有地方接收,详细看PackageIntaller 中的GrantPermissionsActivity(详解点击这里):

            <activity android:name=".permission.ui.GrantPermissionsActivity"
                    android:configChanges="orientation|keyboardHidden|screenSize"
                    android:excludeFromRecents="true"
                    android:theme="@style/GrantPermissions"
                    android:visibleToInstantApps="true">
                <intent-filter android:priority="1">
                    <action android:name="android.content.pm.action.REQUEST_PERMISSIONS" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>

    3、onRequestPermissionResult 有 3 中方式触发,这里是一种,另外是通过:

        private void dispatchRequestPermissionsResult(int requestCode, Intent data) {
            mHasCurrentPermissionsRequest = false;
            // If the package installer crashed we may have not data - best effort.
            String[] permissions = (data != null) ? data.getStringArrayExtra(
                    PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES) : new String[0];
            final int[] grantResults = (data != null) ? data.getIntArrayExtra(
                    PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS) : new int[0];
            onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    
        private void dispatchRequestPermissionsResultToFragment(int requestCode, Intent data,
                Fragment fragment) {
            // If the package installer crashed we may have not data - best effort.
            String[] permissions = (data != null) ? data.getStringArrayExtra(
                    PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES) : new String[0];
            final int[] grantResults = (data != null) ? data.getIntArrayExtra(
                    PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS) : new int[0];
            fragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }

     

    提示用户为什么需要:

        /**
         * Gets whether you should show UI with rationale for requesting a permission.
         * You should do this only if you do not have the permission and the context in
         * which the permission is requested does not clearly communicate to the user
         * what would be the benefit from granting this permission.
         * <p>
         * For example, if you write a camera app, requesting the camera permission
         * would be expected by the user and no rationale for why it is requested is
         * needed. If however, the app needs location for tagging photos then a non-tech
         * savvy user may wonder how location is related to taking photos. In this case
         * you may choose to show UI with rationale of requesting this permission.
         * </p>
         *
         * @param permission A permission your app wants to request.
         * @return Whether you can show permission rationale UI.
         *
         * @see #checkSelfPermission(String)
         * @see #requestPermissions(String[], int)
         * @see #onRequestPermissionsResult(int, String[], int[])
         */
        public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
            return getPackageManager().shouldShowRequestPermissionRationale(permission);
        }

    returns true 如果app以前要求过这个权限,但是用户拒绝了,这个时候 permission还是可以 request 的。
    returns false 如果以前用户拒绝给予这个权限并且在系统对话框中选择了“Don't ask again ”选项。
    returns false 如果device policy 禁止app拥有这个权限。这种通常都是在DevicePolicyManager中设置的。

    • 应用安装后第一次访问,直接返回false;
    • 第一次请求权限时,用户拒绝了,下一次shouldShowRequestPermissionRationale()返回 true,这时候可以显示一些为什么需要这个权限的说明;
    • 第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项时:shouldShowRequestPermissionRationale()返回 false;
    • 设备的系统设置中禁止当前应用获取这个权限的授权,shouldShowRequestPermissionRationale()返回false;

     

    关于自动授权:

    • 带有PRIVATE_FLAG_PRIVILEGED & FLAG_PERSISTENT flag的app会被自动给予授权。
    • 系统默认的基础app会被自动给予相应的permission.比如默认的电话本app自动会拥有CONTACT相关的permission。

    自动授权是通过函数grantDefaultPermissions() @DefaultPermissionGrantPolicy.Java实现的。该函数会在system ready和创建新用户时调用。DefaultPermissionGrantPolicy.java文件是PMS中在M上新增的一个类,用于实现自动授权的相关功能。

     

    permission flags:

    PackageManager.FLAG_PERMISSION_USER_SET: 权限被用户设置,应用还可以在runtime 的时候request
    PackageManager.FLAG_PERMISSION_USER_FIXED:权限被用户设置,但是应用不能再request此权限(用户勾选了“never ask again”)。
    PackageManager.FLAG_PERMISSION_POLICY_FIXED:device policy设定的权限,用户和app都不能修改。
    PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE:如果permission被标记了这个flag,那么表示,app升级后被deny的permission,会依然是deny的状态。这个flag会在下面的情况中用到。适用于L以前版本的app,安装得到M的device上,如果它的dangerous permission被撤销了,比如通过settings里面的permission管理撤销或者device policy中设定,那么该APP升级到适用于M新的permission模式后,那么升级后这个permission依然是撤销的状态。也就是dangerous permission如果在升级之前被撤销过,升级后依然是撤销的状态。
    PackageManager.FLAG_PERMISSION_SYSTEM_FIXED: 系统app获得的自动授权的permission。

    PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT:  默认的系统基本功能app获得的自动授权的permission.

    FLAG_PERMISSION_REVIEW_REQUIRED:在app 运行之前必须要进行permission review

     

    允许权限:

        public void grantRuntimePermission(String packageName, String name, final int userId) {
            grantRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
        }

    详细的source code ,可以看PMS中的具体实现,也可以另一篇博文 grantRuntimePermission 详解

     

    禁止权限:

        @Override
        public void revokeRuntimePermission(String packageName, String name, int userId) {
            revokeRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
        }

    看完 grantRuntimePermission 详解 之后,revokeRuntimePermission 的代码就简单多了,最终同样的是修改PermissionStates中的mGranted 属性,并且将其属性写入runtime-permissions.xml 中。

     

    相关文章:

    android GrantPermissionsActivity 详解

    android grantRuntimePermission 详解

    AppOps 对于Normal permission 的控制

    Android native 权限控制流程

    android M 之前应用权限和M 之后的应用权限控制

    Provider 权限详解

     

     

     

    展开全文
  • Permission(使用与框架使用)

    万次阅读 2019-04-08 19:31:22
       三、Normal Permission和Dangerous Permission    四、实现支持运行时权限    五、permissionsdispatcher框架的使用 一、引言   在Android 6.0以前(targetSdkVersion 23)以前,我们安装APP时会...

    目录

       一、引言
       二、效果
       三、Normal Permission和Dangerous Permission
       四、实现支持运行时权限
       五、permissionsdispatcher框架的使用

    一、引言

      在Android 6.0以前(targetSdkVersion 23)以前,我们安装APP时会列出所有该APP在Manifest中列出的访问权限,而且只会出现一次,一旦我们同意安装此APP并同意所有权限,APP就可以在用户不知情的情况下访问手机中的数据,包括(通讯录、定位)等隐私,在6.0以后,APP在运行时会一个一个询问用户授予权限。


    二、效果

    正常允许权限:
    在这里插入图片描述

    拒绝允许权限的处理
    在这里插入图片描述


    三、Normal Permission和Dangerous Permission

    Google给出的关于权限分类的官方文档,
    https://developer.android.google.cn/guide/topics/security/permissions.html#normal-dangerous

    Dangero Permission以分组形式给出
    在这里插入图片描述

    在Android6.0(API 23) 到8.0(API 26)之间,同一组的任何权限被授权了,其他权限也自动授权,此外对于申请时弹出的提示框上面的文本说明也是对整个权限组的说明,而不是单个权限的说明。但是Android 8.0(APi 26)以后对于危险权限需要一个一个的申请。


    四、实现支持运行时权限

    1、首先是我们拨打电话的点击事件:

     dialBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    call();
                }
            });
    

    2、这时进行权限的检查,是否拥有拨打电话的权限,如果有则进行拨号,如果没有进行权限的申请:

     private void call() {
            //检查APP是否有权限
            if(ActivityCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED){
                //如果没有
                ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CALL_PHONE},PERMISSION_REQURST_CALL_PHONE);
            }else{
                callPhone();
            }
        }
        
        /**
         * 拨打电话
         */
        private void callPhone() {
            Intent intent = new Intent(Intent.ACTION_CALL);
            Uri data = Uri.parse("tel:"+ 10086);
            intent.setData(data);
            startActivity(intent);
        }
    

    3、权限申请回调的处理:

     @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            if(requestCode == PERMISSION_REQURST_CALL_PHONE){
                if(grantResults[0] != 0  && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                //允许申请的权限
                    callPhone();
                }else{
                //申请权限被拒绝
                Toast.makeText(this,"申请权限被拒绝",Toast.SHORTTIME).show();
                    
                }
                return;
            }
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    

    4、处理“不再询问”选项
      在申请权限时,如果我们点击“拒绝”,则下一次还会继续弹出权限申请框,只不过这一次会多出来一项“不再询问”,如果我们勾选了这个选项,则下一次不会再弹出此权限申请框,用户需要去设置—》应用权限列表中手动打开权限。

    在这里插入图片描述

    用户对于已经勾选“不再询问”的权限,每次调用该权限API时都会失效,用户无法使用该功能,这显然不会带来好的用户体验,这个时候我们需要使用shouldShowRequestPermissionRationale()方法向用户解释权限使用情况,并设置进入应用的设置权限所在:
    在这里插入图片描述

     @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            if(requestCode == PERMISSION_REQURST_CALL_PHONE){
                if(grantResults[0] != 0  && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    callPhone();
                }else{
                    if(!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,Manifest.permission.CALL_PHONE)){
                        final AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
                                .setTitle("提示")
                                .setMessage("该功能需要访问电话的权限,否则无法使用")
                                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialogInterface, int i) {
                                        dialogInterface.dismiss();
                                    }
                                })
                                .setPositiveButton("前去设置", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialogInterface, int i) {
                                        //跳转到当前APP的设置页面
                                        Intent intent = new Intent();
                                        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                        intent.addCategory(Intent.CATEGORY_DEFAULT);
                                        intent.setData(Uri.parse("package:" + getPackageName()));
                                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                        intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
                                        intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                                        startActivity(intent);
                                    }
                                }).create();
                        dialog.show();
                    }
                }
                return;
            }
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    

    五、permissionsdispatcher框架的使用

    1、依赖
    在Project的build.gradle 中添加如下代码:

    dependencies {
            ...
            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
         
        }
    

    在app的build.gradle中添加依赖:

    dependencies {
        implementation 'com.github.hotchemi:permissionsdispatcher:2.1.3'
        annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:2.1.3'
    }
    

    permissionsdispatcher以注释的方式进行权限的处理,其中几个重要的注释:

    注释名称注释解释
    RuntimePermissions必要的注释,用来注册一个Activity或者一个Fragment,使他们可以处理权限
    NeedsPermission必要的注释,在需要获取权限的地方进行注释,用来获取权限
    OnShowRationale提示用户为何需要开启此权限,在用户选择拒绝后,再次需要访问该权限时调用
    OnPermissionDenied用户选择拒绝时的提示
    OnNeverAskAgain用户选择不再询问后的提示

    代码如下:

    
    import android.Manifest;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.net.Uri;
    import android.provider.Settings;
    import android.support.annotation.NonNull;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.Toast;
    
    import permissions.dispatcher.NeedsPermission;
    import permissions.dispatcher.OnNeverAskAgain;
    import permissions.dispatcher.OnPermissionDenied;
    import permissions.dispatcher.OnShowRationale;
    import permissions.dispatcher.PermissionRequest;
    import permissions.dispatcher.RuntimePermissions;
    
    @RuntimePermissions
    public class SecondActivity extends AppCompatActivity {
    
        private Button dialBtn;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
            initView();
        }
    
        private void initView() {
            dialBtn = (Button) findViewById(R.id.dial_btn);
            dialBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                  
                }
            });
        }
    
        @NeedsPermission(Manifest.permission.CALL_PHONE)
        //在需要获取权限的地方注释
        void call(){
            Intent intent = new Intent(Intent.ACTION_CALL);
            Uri data = Uri.parse("tel:" + 10086);
            intent.setData(data);
            startActivity(intent);
        }
    
        @OnShowRationale(Manifest.permission.CALL_PHONE)
        //提示用户为何要开启权限
        void showWhy(final PermissionRequest request){
            new AlertDialog.Builder(SecondActivity.this)
                    .setMessage("提示用户为何要开启权限")
                    .setPositiveButton("知道了", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            request.proceed(); //再次执行权限请求
                        }
                    })
                    .show();
        }
    
        @OnPermissionDenied(Manifest.permission.CALL_PHONE)
        void showDenied(){
            Toast.makeText(SecondActivity.this,"用户选择拒绝时的提示",Toast.LENGTH_SHORT).show();
        }
    
        @OnNeverAskAgain(Manifest.permission.CALL_PHONE)
        void showNotAsk(){
            new AlertDialog.Builder(SecondActivity.this)
                    .setMessage("该功能需要访问电话的权限,不开启将无法使用该功能")
                    .setPositiveButton("前去设置", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            //跳转到当前APP的设置页面
                            Intent intent = new Intent();
                            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                            intent.addCategory(Intent.CATEGORY_DEFAULT);
                            intent.setData(Uri.parse("package:" + getPackageName()));
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
                            intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                            startActivity(intent);
                        }
                    })
                    .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.dismiss();
                        }
                    })
                    .show();
        }
    
    }
    

    这个时候我们编译程序,会生成一个辅助类SecondActivityPermissionsDispatcher,下面的事情由它进行解决,完整代码:

    @RuntimePermissions
    public class SecondActivity extends AppCompatActivity {
    
        private Button dialBtn;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
            initView();
        }
    
        private void initView() {
            dialBtn = (Button) findViewById(R.id.dial_btn);
            dialBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
               //*****************重点*****************
               SecondActivityPermissionsDispatcher.callWithCheck(SecondActivity.this);
                }
            });
        }
    
        @NeedsPermission(Manifest.permission.CALL_PHONE)
        //在需要获取权限的地方注释
        void call(){
            Intent intent = new Intent(Intent.ACTION_CALL);
            Uri data = Uri.parse("tel:" + 10086);
            intent.setData(data);
            startActivity(intent);
        }
    
        @OnShowRationale(Manifest.permission.CALL_PHONE)
        //提示用户为何要开启权限
        void showWhy(final PermissionRequest request){
            new AlertDialog.Builder(SecondActivity.this)
                    .setMessage("提示用户为何要开启权限")
                    .setPositiveButton("知道了", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            request.proceed(); //再次执行权限请求
                        }
                    })
                    .show();
        }
    
        @OnPermissionDenied(Manifest.permission.CALL_PHONE)
        void showDenied(){
            Toast.makeText(SecondActivity.this,"用户选择拒绝时的提示",Toast.LENGTH_SHORT).show();
        }
    
        @OnNeverAskAgain(Manifest.permission.CALL_PHONE)
        void showNotAsk(){
            new AlertDialog.Builder(SecondActivity.this)
                    .setMessage("该功能需要访问电话的权限,不开启将无法使用该功能")
                    .setPositiveButton("前去设置", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            //跳转到当前APP的设置页面
                            Intent intent = new Intent();
                            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                            intent.addCategory(Intent.CATEGORY_DEFAULT);
                            intent.setData(Uri.parse("package:" + getPackageName()));
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
                            intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                            startActivity(intent);
                        }
                    })
                    .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.dismiss();
                        }
                    })
                    .show();
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
                //*****************重点*****************
                SecondActivityPermissionsDispatcher.onRequestPermissionsResult(SecondActivity.this,requestCode,grantResults);
        }
    }
    
    

    OK ,这是权限里面的内容了,希望能帮到你

    展开全文
  • Android权限Permission的详解

    千次阅读 2019-05-09 19:30:18
    访问登记属性 android.permission.ACCESS_CHECKIN_PROPERTIES ,读取或写入登记check-in数据库属性表的权限 获取错略位置 android.permission.ACCESS_COARSE_LOCATION,通过WiFi或移动基站的方式获取用户错略的...
  • Android 权限(permission)大全

    千次阅读 2018-09-21 15:57:24
    1.android.permission.WRITE_USER_DICTIONARY 允许应用程序向用户词典中写入新词  2.android.permission.WRITE_SYNC_SETTINGS 写入Google在线同步设置  3.android.permission.WRITE_SOCIAL_STREAM 读取用户的...
  • uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/&gt;public static final String WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";...
  • uses-permissionpermission详解

    千次阅读 2019-08-02 06:50:18
    uses-permission>: 官方描述: If an application needs access to a feature protected by a permission, it must declare that it requires that permission with a <uses-permission> element in.....
  • Android权限(permission)大全

    千次阅读 2017-06-08 12:42:43
    1.android.permission.WRITE_USER_DICTIONARY 允许应用程序向用户词典中写入新词 2.android.permission.WRITE_SYNC_SETTINGS 写入Google在线同步设置 3.android.permission.WRITE_SOCIAL_STREAM 读取用户的社交...
  • Android各种访问权限Permission详解

    千次阅读 2019-03-15 10:33:24
    访问网络 android.permission.INTERNET,访问网络连接,可能产生GPRS流量 结束后台进程 android.permission.KILL_BACKGROUND_PROCESSES,允许程序调用killBackgroundProcesses(String).方法结束后台进程 管理账户 ...
  • 问题描述: ...PermissionError: [Errno 13] Permission denied:。。。 原因分析: python脚本在往csv或xsl文件写入数据时手动打开了文件导致写入终止。 解决办法: 关闭手动打开的文件,重新运行即可 ...
  • Android权限操作之uses-permission详解

    千次阅读 2019-06-04 20:17:25
    本文实例讲述了Android权限操作之uses-permission。分享给大家供大家参考,具体如下: 我们在安装Android软件的时候,系统会提示该软件所需要的权限,相对于其他系统,android的权限非常多。我们在开发软件的时候,...
  • Android之权限(permission)大全

    万次阅读 2018-11-20 09:09:24
    Android 6.0开始,Google将权限分为两类,一类是Normal Permission, 这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要...
  • laravel权限管理permission

    千次阅读 2018-04-24 15:39:25
    https://github.com/spatie/laravel-permission 中文翻译: https://laravel-china.org/topics/8018/extension-recommendation-role-and-authority-control-in-spatielaravel-permission-laravel-applications .....
  • 解决某些Android Permission denied

    千次阅读 2018-03-12 10:49:40
    Android Permission denied 错误 Android Permission denied :这是个很头疼的错误,我因为这个错误弄了3天最后才弄好。先说说我的错误,我是读写SD卡的时候报错的。加上权限 &lt;uses-permission android:...
  • Android permission权限详解

    万次阅读 2018-09-29 17:13:13
    权限是一种安全机制。Android权限主要用于限制应用程序内部某些具有限制性特性的功能使用以及应用程序之间...uses-permission android:name="android.permission.INTERNET" /&gt;  实际上,在开发过...
  • 高德地图当前定位失败Geolocation permission denied 代码来自官网 ``` mapObj = new AMap.Map('iCenter'); mapObj.plugin('AMap.Geolocation', function () { geolocation = new AMap.Geolocation({ ...
  • yanzhenjie:permission:1.0.5 使用

    千次阅读 2020-03-01 21:14:15
     // AndPermission.with(this).requestCode(102).permission(Manifest.permission.WRITE_EXTERNAL_STORAGE).send();  } // 成功回调的方法,用注解即可,里面的数字是请求时的requestCode。  // 第一个权限...
  • //cczheng annotation for don't check android.Manifest.permission.CAPTURE_AUDIO_OUTPUT /*ALOGE("getInputForAttr() permission denied: capture not allowed"); status = PERMISSION_DENIED;*/ } ...
  • vuex使用及自定义Vue指令vue-permission

    千次阅读 2020-10-24 23:26:04
    并将permission指令导出: import permission from './permission' const install = function(Vue) { Vue.directive('permission', permission) } if (window.Vue) { window['permission'] = permission Vue.use...
  • Mac 使用 之 Permission denied XXX

    万次阅读 2019-06-11 00:35:50
    LZ-Says:守好自己一亩三分地即可。 前言 一个人; 一,个人; 一个,人。...再浪,真成人嘴里说的了。...某天,破解某个软件时,需要拖拽文件,But,提示如下: ...很明显,权限不足,如果要使用,通过如下命令进行授权...
  • 解决 550 Permission denied

    千次阅读 2021-01-26 19:59:18
    解决 550 Permission denied 我在跟着大神@良许Linux 的教程学习Linux之“使用NOTEPAD++”远程编辑虚拟机文档时,遇到 550 Permission denied问题。 我已经成功配置FTP,并且在主机上能打开Linux虚拟机的共享文档: ...
  • django自带的权限管理Permission用法

    千次阅读 2020-04-07 18:03:45
    Permission的用法前言默认的权限(add, change, delete, view)使用方法在函数中验证权限,使用user.has_perm在函数上验证权限,使用@permission_required 前言 一些公司内部的CMS系统存在某些内容让指定的用户有...
  • 此文章根据 laravel-permission官方说明文件 翻译管理你的用户对应的角色与权限此软件包允许你管理数据库中的用户权限和角色。安装后,你可以做这样的事情:// 向用户添加权限 $user-&gt;givePermissionTo('...
  • Android Permission介绍

    万次阅读 2016-10-31 11:16:55
    签名权限(signature permission):该类权限只对拥有相同签名的应用开放,比如手Q程序自定义了一个permission,微信要去访问QQ的某个数据时,必须要拥有该权限,那么手Q在自定义该权限时可以在权限标签中加入android:...
  • 解决Android11 adb push Permission denied

    千次阅读 2020-11-23 22:03:10
    这里写自定义目录标题Android11 adb push Permission denied解决方法 Android11 adb push Permission denied 在Android11上使用adb进行push文件到手机内存卡的时候报错Permission denied,意思是没权限。 解决方法 ...
  • flutter ios permission_handle权限动态申请

    千次阅读 2020-01-27 21:44:58
    文章目录配置permission_handleAndroid项目IOS项目代码 网上都是针对安卓的教程,以下是作者使用Flutter打包在iphone上运行动态申请权限的实战记录。 项目在:https://github.com/xmcy0011/CoffeeChat 开源IM解决...
  • Linux ftp 上传文件 提示 Permission denied 解决办法

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 350,494
精华内容 140,197
关键字:

permission