Android 6.0 权限 运行流畅 介绍
总所周知,Android 在6.0的时候添加了权限申请机制,默认情况下,系统是没有给你这些权限的。在你需要使用这些权限的时候,是需要申请了,不然会很悲剧。
举个栗子
比如你需要定位,如果你不获取定位权限,那么你是获取到的经纬度可能是0 and 0
在比如,在分享的时候,如果缺少对应的权限,会直接失败
在比如 …(此处省略1万个字)
总之运行时权限的申请在6.0越来越普及的今天,已经成为一个必不可少的技能了。
关于6.0权限详细解析,请参考下面几篇文献,这里篇文章主要介绍一下权限申请时候的流程。
详解Android 6.0运行时权限
Android 6.0运行时权限详解
首先先放两张效果图
两部手机运行的程序为一份代码,且都是7.0的系统 ,但是由于厂家优化问题,所呈现的效果有着巨大的差异。这里我使用Google 亲儿子作为讲解。
首先要先介绍三个方法
checkSelfPermission
/**
* Determine whether <em>you</em> have been granted a particular permission.
*
* @param permission The name of the permission being checked.
*
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
* permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
*
* @see android.content.pm.PackageManager#checkPermission(String, String)
*/
public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
}
我相信看不懂这堆鸟文的不只我一个 ,现在我就来解释解释。
这个方法主要是用于检测 是否具备某个权限。
如果返回值 == PackageManager.PERMISSION_GRANTED
,那就恭喜你,app已经获取到了相应的权限。
相反,如果 != 则需要申请权限。
举个栗子
ActivityCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
shouldShowRequestPermissionRationale
这个方法很有意思
/**
* 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 activity The target activity.
* @param permission A permission your app wants to request.
* @return Whether you can show permission rationale UI.
*
* @see #checkSelfPermission(android.content.Context, String)
* @see #requestPermissions(android.app.Activity, String[], int)
*/
public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
@NonNull String permission) {
if (Build.VERSION.SDK_INT >= 23) {
return ActivityCompatApi23.shouldShowRequestPermissionRationale(activity, permission);
}
return false;
}
英语好的可以直接啃api 。
这个方法我的理解是这样的
默认返回 false ,也就是什么都没发生的时候;
被拒绝了 ,用户没有勾选 do't ask again
,返回 true;
被do't ask again
拒绝了,返回false;
个人认为这个申请API中最麻烦的就是这玩意了。
requestPermissions
//这个源码太多了,我就不放了
requestPermissions(final @NonNull Activity activity,final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode)
这个方法主要作用是用于申请权限
从参数 String[] permissions
上可以发现,这个方法可以一次性申请多个权限,但是官方并不推荐这样做。
同样,参数 requestCode
说明这种申请方式 与 startActivityForResult
是一样的。
友情提醒, requestCode
不要太大,Can only use lower 16 bits for requestCode
onRequestPermissionsResult
onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults)
这个方法是 FragmentActivity
中的方法,所以如果申请了权限,需要重写这个方法来获取申请的结果。
最终申请的结果都放在了 grantResults
之中,如果这个数组中的值 == PackageManager.PERMISSION_GRANTED
,就说明获取了权限,反之亦然。
栗子
首先要申请权限,那就需要现在Manifest中进行注册,比如我想GET这两个权限。
<!--用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--用于访问GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
在主页面中建个按钮,用于开始申请权限。代码很简单,我就不贴了。
在代码中先定义这两个权限
String pNet = Manifest.permission.ACCESS_FINE_LOCATION;
String pGPS = Manifest.permission.ACCESS_COARSE_LOCATION;
因为会多次使用,所以先偷个懒先。
点击事件代码
@Override
public void onClick(View v)
{
if (ActivityCompat.checkSelfPermission(this, pNet) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, pGPS) != PackageManager.PERMISSION_GRANTED)
{
if (shouldShowRequest())
{
//被拒绝了一次
mHintDialog.setMessage("被拒绝过了,但是还是可以申请权限")//
.setPositiveButton("确定", new RequestPermissionsClick())//
.show();
} else
{
//第一次申请权限
//被无限拒绝
//都会到这里来
mHintDialog.setMessage("我想要个权限申请权限")//
.setPositiveButton("确定", new RequestPermissionsClick())//
.show();
}
} else
{
mHintDialog.setMessage("你已经拥有权限了,不用瞎申请了")//
.setPositiveButton("确定", null)//
.show();
}
}
shouldShowRequest()
private boolean shouldShowRequest()
{
return ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_COARSE_LOCATION) && ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION);
}
RequestPermissionsClick
private class RequestPermissionsClick implements DialogInterface.OnClickListener
{
@Override
public void onClick(DialogInterface dialog, int which)
{
//这里是不提倡的写法
String[] p = {pNet, pGPS};
ActivityCompat.requestPermissions(MainActivity.this, p, GET_LOCATION_REQUEST_CODE);
}
}
首先 通过 checkSelfPermission
判断是否拥有权限
在没有权限的情况下
可以通过判断 shouldShowRequestPermissionRationale
判断状态
如果返回 false ,说明可能是第一次,也可能是被宣判再也无法申请权限了。
如果返回 true , 说明被拒绝过,还可以在申请权限
最后都点击事件都执行 requestPermissions
进行权限的申请。
一般情下,在 shouldShowRequestPermissionRationale
返回fasle的时候我们会告诉用户我们需要什么权限。当然,用作可能并不会给予我们权限,所以在返回true的时候,我们还有补救的机会。也就是像用户解释,我们为什么需要这个权限。
onRequestPermissionsResult
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == GET_LOCATION_REQUEST_CODE)
{
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
mHintDialog.setMessage("获取权限成功")//
.setPositiveButton("确定", null)//
.show();
} else
{
if (shouldShowRequest())
{
//被拒绝了一次
mHintDialog.setMessage("虽然被拒绝了,但是还有的救")
.setPositiveButton("确定", new RequestPermissionsClick())//
.show();
} else
{
//已经被无限拒绝了
mHintDialog.setMessage("已经被彻底拒绝了")
.setPositiveButton("确定", new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
Uri packageURI = Uri.parse("package:" + MainActivity.this.getPackageName());
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
startActivity(intent);
}
})//
.show();
}
}
}
}
到这里 shouldShowRequestPermissionRationale 就不会出现应为默认而返回false 的情况下了,所以 当 shouldShowRequestPermissionRationale 返回false,就代表真的无法申请权限了,只有让用户自己开启权限。如果返回true,还可以通过提示引导用户授予权限。
需要注意的是 grantResults.length
是有可能为0的,也就是可能为空。
至此,权限申请的全部流程已经讲解完毕,最后附上鄙人的Code:
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.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
String pNet = Manifest.permission.ACCESS_FINE_LOCATION;
String pGPS = Manifest.permission.ACCESS_COARSE_LOCATION;
//Can only use lower 16 bits for requestCode
final int GET_LOCATION_REQUEST_CODE = 123;
AlertDialog.Builder mHintDialog;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn).setOnClickListener(this);
mHintDialog = new AlertDialog.Builder(this)//
.setTitle("提示")//
;
}
@Override
public void onClick(View v)
{
if (ActivityCompat.checkSelfPermission(this, pNet) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, pGPS) != PackageManager.PERMISSION_GRANTED)
{
if (shouldShowRequest())
{
//被拒绝了一次
mHintDialog.setMessage("被拒绝过了,但是还是可以申请权限")//
.setPositiveButton("确定", new RequestPermissionsClick())//
.show();
} else
{
//第一次申请权限
//被无限拒绝
//都会到这里来
mHintDialog.setMessage("我想要个权限申请权限")//
.setPositiveButton("确定", new RequestPermissionsClick())//
.show();
}
} else
{
mHintDialog.setMessage("你已经拥有权限了,不用瞎申请了")//
.setPositiveButton("确定", null)//
.show();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == GET_LOCATION_REQUEST_CODE)
{
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
mHintDialog.setMessage("获取权限成功")//
.setPositiveButton("确定", null)//
.show();
} else
{
if (shouldShowRequest())
{
//被拒绝了一次
mHintDialog.setMessage("虽然被拒绝了,但是还有的救")
.setPositiveButton("确定", new RequestPermissionsClick())//
.show();
} else
{
//已经被无限拒绝了
mHintDialog.setMessage("已经被彻底拒绝了")
.setPositiveButton("确定", new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
Uri packageURI = Uri.parse("package:" + MainActivity.this.getPackageName());
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
startActivity(intent);
}
})//
.show();
}
}
}
}
private boolean shouldShowRequest()
{
return ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_COARSE_LOCATION) && ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION);
}
private class RequestPermissionsClick implements DialogInterface.OnClickListener
{
@Override
public void onClick(DialogInterface dialog, int which)
{
String[] p = {pNet, pGPS};
ActivityCompat.requestPermissions(MainActivity.this, p, GET_LOCATION_REQUEST_CODE);
}
}
}