android 6.0 有些危险权限需要运行时手动获取才能继续执行应用,否则将出来很多崩溃问题
如果sdk设置22是可以在启动时避免这个问题,先获取到全部的权限,但是一旦用户关闭了你需要的权限,呢完了。你的程序还是奔溃了。用户如果不知道是因为自己关闭了权限导致应用奔溃的。呢就陷入死循环的奔溃中,所以还是先加上这个控制比较好。
以下是通过android系统提供的在运行时获取权限的办法,这里我就不写了网上也有不少我就复制来给大家看看。大家看过了解后我会给大家推荐一个我现在正在使用并且感觉比较好的一款权限管理框架。
当你的这个activity某个功能需要调用到权限的时候(比如,相机,文件读写) 你需要在调用功能前先去检查下权限
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
}else{
//这是已经获得到权限 可以写你的逻辑了
}
这里涉及到一个API,
ContextCompat.checkSelfPermission
,主要用于检测某个权限是否已经被授予,方法返回值为
PackageManager.PERMISSION_DENIED
或者
PackageManager.PERMISSION_GRANTED
。当返回DENIED就需要进行申请授权了。
这是运行时去获得授权的方法
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以从方法名
requestPermissions
以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框
逐一询问用户是否授权。
处理权限申请回调
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
}
}
如果用户拒绝了你的请求,但是没有选择不再提示
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS))
这个API可以让你在次请求的时候,判断是不是已经拒绝过一次了,如果已经拒绝了这是你可以在这里给用户一些提示,为什么需要这个权限
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// 写上你的提示。然后再去请求权限,比如一个提示框,然后告诉用户为什么需要这个权限,用户点击确定后再去调用ActivityCompat请求一遍权限
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
系统的方法就讲到这里。系统的权限请求 activity和fragment略有不同。
//activity
ActivityCompat.requestPermissions(conent, perms, requestCode);
//fragment
fragment.requestPermissions(perms, requestCode);
以上的代码 鸿神的博客写的比较详细大家可以去鸿神呢里看看 传送门 http://blog.csdn.net/lmj623565791/article/details/50709663
鸿神也在后边详解了两个框架
PermissionGen 和 MPermissions 第一个是用反射实现的,但是这两个框架的代码稍稍多了一点,对我来说不宜学习。所以我推荐一个更简洁的框架,这个只需要一个类,一个注解就决绝了以上的问题。
这个框架就是
EasyPermission 大家也可以去网上找找这个框架的使用,我在这里就简单的介绍一下使用
activity或者fragment需要实现这个接口
EasyPermissions.PermissionCallbacks
@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
//这是回掉成功的方法,成功的需要判断请求成功几条权限,可以用list的条数进行判断 请求的条数等于成功数这样就是全部请求成功了
}
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
//这是失败的
}
}
这是请求时的代码
@AfterPermissionGranted(REQUECT_CODE_SDCARD) //请求时的请求码
private void getPermiss(){
if (EasyPermissions.hasPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE})) {
} else {
EasyPermissions.requestPermissions(this,"没有权限将无法继续执行,请为应用授权!", REQUECT_CODE_SDCARD, permission);
}
}
然后就是系统的回掉方法,你需要调用框架的回掉方法,把系统的回掉方法的值传给它
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
这样一个权限请求就写完了。当你的应用需要用到这些权限的时候。调用getPermiss方法就可以。
这里附上一个使用示例:
import android.Manifest;
import android.app.Activity;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.NonNull;
import com.jiufu.fenqigo.R;
import com.jiufu.fenqigo.utils.AfterPermissionGranted;
import com.jiufu.fenqigo.utils.EasyPermissions;
import java.util.List;
/**
* Created by grove on 2016/9/23.
*/
public class TestPermission extends Activity implements EasyPermissions.PermissionCallbacks {
public static final String READ_EXTERNAL_STORAGE= Manifest.permission.READ_EXTERNAL_STORAGE;//读取外部存储器
public static final String WRITE_EXTERNAL_STORAGE= Manifest.permission.WRITE_EXTERNAL_STORAGE;//写入外部存储器
public static final String CAMERA= Manifest.permission.CAMERA;//相机
private static final int REQUECT_CODE_SDCARD = 4;//请求码
private String []permission={WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE,CAMERA};
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
//这个界面开启就检查权限,有就执行逻辑,没有就去去那个请求权限
//这个方法你也可以写在你需要权限操作的事件里
getPermiss();
}
//比如用到调用相机,调用的方法写在getPermiss()方法里边
@AfterPermissionGranted(REQUECT_CODE_SDCARD)
private void getPermiss(){
//检查是否已经赋予了所有权限
if (EasyPermissions.hasPermissions(this, permission)) {
// 成功
//自己的逻辑(比如调用相机)
} else {
//有权限没有被赋予去请求权限
EasyPermissions.requestPermissions(this, getString(R.string.permissonTxt), REQUECT_CODE_SDCARD, permission);
}
}
//重写onRequestPermissionsResult将值传递给EasyPermissions.onRequestPermissionsResult();
//固定写法
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
//请求后的回掉,成功
@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
//判断请求成功的权限数量和需要请求的权限数量是否相同,相同表示全部权限都请求成功
if (perms.size()==permission.length){
//获得或有权限后的逻辑
}
}
//失败
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
//这里返回请求失败的权限
//这里的逻辑可写可不写。请随意。
}
}
后面我会复上这个框架的类。
现在框架都讲完了。其实会遇到一个问题,呢就是如果用户点了拒绝,然后也选择了不再提醒,但是我们的应用有必须需要这个权限,呢怎么办呢,其实可以在框架中稍稍修改下就可以解决这个问题,当不能再弹出来提示框的时候,我们可以给一个提示框,然后让用户到系统设置我们应用的界面里,去手动的打开这个权限:
private static int count;//定义一个记录请求次数的值
public static void requestPermissions(final Object object, String rationale,
@StringRes int positiveButton,
@StringRes int negativeButton,
final int requestCode, final String... perms) {
checkCallingObjectSuitability(object);
final PermissionCallbacks callbacks = (PermissionCallbacks) object;
boolean shouldShowRationale = false;
for (String perm : perms) {
shouldShowRationale = shouldShowRationale || shouldShowRequestPermissionRationale(object, perm);
}
if (shouldShowRationale) { //用户虽然点了拒绝,但是没有选择不再提示,这样每次都会弹这个框框
AlertDialog dialog = new AlertDialog.Builder(getActivity(object))
.setMessage(rationale)
.setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
executePermissionsRequest(object, perms, requestCode);
}
})
.setNegativeButton(negativeButton, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// act as if the permissions were denied
callbacks.onPermissionsDenied(requestCode, Arrays.asList(perms));
}
}).create();
dialog.show();
} else {
if (count==0){//第一次请求
executePermissionsRequest(object, perms, requestCode);
count=1;
}else {//如果选择了不再提示,呢样就会进入这里,弹出提示框,让他直接去系统设置中打开权限
AlertDialog dialog = new AlertDialog.Builder(getActivity(object))
.setMessage(perminssions_content)
.setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Uri packageURI = Uri.parse("package:" + getActivity(object).getPackageName().toString());
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
getActivity(object).startActivity(intent);
}
})
.setNegativeButton(negativeButton, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).create();
dialog.show();
}
}
}
要记得权限请求成功后,吧count还原成0,要不别的页面就不能正常请求了。在这个方法里边还原
public static void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults, Object object) {
checkCallingObjectSuitability(object);
PermissionCallbacks callbacks = (PermissionCallbacks) object;
// Make a collection of granted and denied permissions from the request.
ArrayList<String> granted = new ArrayList<>();
ArrayList<String> denied = new ArrayList<>();
for (int i = 0; i < permissions.length; i++) {
String perm = permissions[i];
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
granted.add(perm);
} else {
denied.add(perm);
}
}
// Report granted permissions, if any.
if (!granted.isEmpty()) {
// Notify callbacks
count=0;//请求成功,还原count
callbacks.onPermissionsGranted(requestCode, granted);
}
// Report denied permissions, if any.
if (!denied.isEmpty()) {
callbacks.onPermissionsDenied(requestCode, denied);
}
// If 100% successful, call annotated methods
if (!granted.isEmpty() && denied.isEmpty()) {
runAnnotatedMethods(object, requestCode);
}
}
这个框架只有一个类,相对学习起来都比较容易,看完这个代码,自己对权限这块就会比较清晰了。
但是权限的这些API这不同的手机上的呈现都有不同。这个的兼容问题,就要靠大家的努力一起解决了。
比如我现在就遇到了,在小米上,同时请求手机及状态和通讯录通话记录读取的时候,用户点击了拒绝,但是权限检查权限的方法却返回了成功。
这个问题我还没解决,大家如果有好的办法,请在留言板回复。
这里我为大家提供一些便利。我将需要运行检查的呢些权限都写成常量放在一起了。也方便以后找那些权限。
/**
* Dangerous Permissions
* 坑爹的危险权限
* */
public static final String WRITE_CONTACTS= Manifest.permission.WRITE_CONTACTS;//写入联系人,但不可读取
public static final String GET_ACCOUNTS= Manifest.permission.GET_ACCOUNTS;//访问一个帐户列表在Accounts Service中
public static final String READ_CONTACTS= Manifest.permission.READ_CONTACTS;//读取联系人,但不可写入
public static final String READ_CALL_LOG= Manifest.permission.READ_CALL_LOG;//读取通话记录
public static final String READ_PHONE_STATE= Manifest.permission.READ_PHONE_STATE;//读取手机状态
public static final String CALL_PHONE= Manifest.permission.CALL_PHONE;//读取手机号码
public static final String WRITE_CALL_LOG= Manifest.permission.WRITE_CALL_LOG;//写入通话记录
public static final String USE_SIP= Manifest.permission.USE_SIP;
public static final String PROCESS_OUTGOING_CALLS= Manifest.permission.PROCESS_OUTGOING_CALLS;//允许程序监视、修改有关播出电话
public static final String ADD_VOICEMAIL= Manifest.permission.ADD_VOICEMAIL;
public static final String READ_CALENDAR= Manifest.permission.READ_CALENDAR;//允许程序读取用户日历数据
public static final String WRITE_CALENDAR= Manifest.permission.WRITE_CALENDAR;//允许一个程序写入但不读取用户日历数据
public static final String CAMERA= Manifest.permission.CAMERA;//相机
public static final String BODY_SENSORS= Manifest.permission.BODY_SENSORS;//人体传感器;
public static final String ACCESS_FINE_LOCATION= Manifest.permission.ACCESS_FINE_LOCATION;//这个权限用于访问GPS定位
public static final String ACCESS_COARSE_LOCATION= Manifest.permission.ACCESS_COARSE_LOCATION;//这个权限用于进行网络定位
public static final String READ_EXTERNAL_STORAGE= Manifest.permission.READ_EXTERNAL_STORAGE;//读取外部存储器
public static final String WRITE_EXTERNAL_STORAGE= Manifest.permission.WRITE_EXTERNAL_STORAGE;//写入外部存储器
public static final String RECORD_AUDIO= Manifest.permission.RECORD_AUDIO;//允许程序录制音频
public static final String READ_SMS= Manifest.permission.READ_SMS;//读取短信
public static final String RECEIVE_WAP_PUSH= Manifest.permission.RECEIVE_WAP_PUSH;//允许程序监控将收到WAP PUSH信息
public static final String RECEIVE_MMS= Manifest.permission.RECEIVE_MMS;//允许一个程序监控将收到MMS彩信,记录或处理
public static final String RECEIVE_SMS= Manifest.permission.RECEIVE_SMS;//允许程序监控一个将收到短信息,记录或处理
public static final String SEND_SMS= Manifest.permission.SEND_SMS;//允许程序发送SMS短信
EasyPermiss项目地址