Android API23以上的时候,出现了动态请求权限的APP行为,敏感权限要求用户自己选择允许或拒绝,以及如何提示用户开启相关服务。此文章以定位权限和服务为例。
在AS开发中,可能会遇到一个坑,就是即使写明了动态请求逻辑,还是唤不起系统的权限请求弹窗。在经过漫长的查找过程后,终于发现了原因:必须在build-config.gradle文件中将targetSDKVersion声明为23或者以上:
ext {
config = [
//app、sdk版本信息
compileSdkVersion : 25,
minSdkVersion : 14,
targetSdkVersion : 23,//22
multidex : "1.0.3",
gsonVersion : "2.8.4",
supportLibVersion : "25.3.1",
constraintLayoutVersion : "1.1.2",
orgApacheCommonsLangVersion: "2.6",
baseLibrary : ":uhome_base",
]
}
升级之后要注意,所有原本清单里声明的敏感权限在应用安装之后都是默认关闭的,都需要进行动态申请。动态申请的权限,在清单文件里也必须声明。
Activity中权限处理流程(在判断有定位权限的情况下,再判断是否开启了定位服务,因定位服务的判断会同时判断定位权限,顺序不能颠倒):
@TargetApi(Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.community_my);
initView();
initDatas();
checkPermissions();//判断定位权限和服务,然后定位
}
checkPermissions()方法定义如下:
/**
* 获取定位权限
*/
@RequiresApi(api = Build.VERSION_CODES.M)
private void checkPermissions() {
PackageManager pm = getPackageManager();
List<String> needRequestPermissonList = new ArrayList<>();
for (String perm : permissions2) {
if (pm.checkPermission(perm, getPackageName()) != PackageManager.PERMISSION_GRANTED) {//ContextCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED || ActivityCompat.shouldShowRequestPermissionRationale(this, perm)
needRequestPermissonList.add(perm);
}
}
// show("size : " + needRequestPermissonList.size());
//调用系统弹窗
if (needRequestPermissonList.size() > 0) {
ActivityCompat.requestPermissions(this,
needRequestPermissonList.toArray(new String[needRequestPermissonList.size()]),
PERMISSON_REQUESTCODE);
}else {
//有定位权限的情况下,再判断是否开启了定位服务,因定位服务的判断会同时判断定位权限,顺序不能颠倒
checkLocationServices();
}
}
其中permissions2取自百度定位文档中提出的需要动态获取权限 :
private String[] permissions2 = {
"android.permission.CALL_PHONE",
"android.permission.ACCESS_COARSE_LOCATION",
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.READ_EXTERNAL_STORAGE"
};
checkLocationServices()定义如下:
/**
* 判断是否有定位服务
*/
private void checkLocationServices() {
LocationManager locationManager = (LocationManager) getSystemService(this.LOCATION_SERVICE);
//获取所有可用的位置提供器
List<String> providers = locationManager.getProviders(true);
String locationProvider = null;
boolean hasGPS = providers.contains(LocationManager.GPS_PROVIDER);
boolean hasNETWORK = providers.contains(LocationManager.NETWORK_PROVIDER);
//show("GPS : " + hasGPS + "; NETWORK : " + hasNETWORK);
if (hasGPS || hasNETWORK) {
//开启了定位服务和获取了定位权限
startLocation();
} else {
showDialog(R.string.request_locat_service, new OnDailogListener() {
@Override
public void onPositiveButton() {
//跳转开启定位服务页
Intent i = new Intent();
i.setAction(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivityForResult(i, SETTING_REQUESTCODE);
}
@Override
public void onNegativeButton() {
stopLocation();
//拒绝打开定位服务,缺省是深圳市
curCity.setText("深圳市");
}
});
}
}
回调:
/**
* 权限获取回调
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] paramArrayOfInt) {
if (requestCode == PERMISSON_REQUESTCODE) {
//获得权限
if (verifyPermissions(paramArrayOfInt)) {
startLocation();
}else {
//没有权限时的操作
}
}
}
/**
* 检测是否所有的权限都已经授权
*/
private boolean verifyPermissions(int[] grantResults) {
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
ps:此Activity封装了百度定位的操作:
/**
* 定位基类Activity
*/
public abstract class BaseLocationActivity extends BaseActivity {
protected LocationService locationService;
protected MyOrientationListener orientationListener;
protected BDLocation mLocation;
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
// -----------location config ------------
locationService = UHomeApp.mLocationClient;
//获取locationservice实例,建议应用中只初始化1个location实例,然后使用,可以参考其他示例的activity,都是通过此种方式获取locationservice实例的
locationService.registerListener(mListener);
//注册监听
locationService.setLocationOption(locationService.getDefaultLocationClientOption());
}
@Override
protected void onResume() {
super.onResume();
locationService.registerListener(mListener);
}
@Override
protected void onPause() {
super.onPause();
locationService.unregisterListener(mListener); //注销掉监听
stopLocation(); //停止定位服务
}
@Override
protected void onStop() {
super.onStop();
stopLocation();
}
@Override
public void onDestroy() {
super.onDestroy();
locationService.unregisterListener(mListener);
}
/**
* 定位成功回调
* @param location
*/
protected abstract void locationSuccessResult(BDLocation location);
/**
* 定位失败回调
* @param location
*/
protected abstract void locationFailResult(BDLocation location);
/**
* 开启定位
*/
protected void startLocation() {
if (null != locationService) {
locationService.start();// 定位SDK
}
}
/**
* 关闭定位
*/
protected void stopLocation() {
if (null != locationService) {
locationService.stop();// 定位SDK
}
}
/**
* 设置方向传感器监听
*/
protected void setOrientationListener() {
orientationListener = new MyOrientationListener(this);
orientationListener.setOnOrientationListener(new MyOrientationListener.OnOrientationListener()
{
@Override
public void onOrientationChanged(float x)
{
if(null != mLocation){
// addMyLocation((int) x);
}
}
});
// 开启方向传感器
orientationListener.start();
}
/*****
* 61 : GPS定位结果,GPS定位成功。
* 62 : 无法获取有效定位依据,定位失败,请检查运营商网络或者wifi网络是否正常开启,尝试重新请求定位。
* 63 : 网络异常,没有成功向服务器发起请求,请确认当前测试手机网络是否通畅,尝试重新请求定位。
* 65 : 定位缓存的结果。
* 66 : 离线定位结果。通过requestOfflineLocaiton调用时对应的返回结果。
* 67 : 离线定位失败。通过requestOfflineLocaiton调用时对应的返回结果。
* 68 : 网络连接失败时,查找本地离线定位时对应的返回结果。
* 161: 网络定位结果,网络定位定位成功。
* 162: 请求串密文解析失败。
* 167: 服务端定位失败,请您检查是否禁用获取位置信息权限,尝试重新请求定位。
* 502: key参数错误,请按照说明文档重新申请KEY。
* 505: key不存在或者非法,请按照说明文档重新申请KEY。
* 601: key服务被开发者自己禁用,请按照说明文档重新申请KEY。
* 602: key mcode不匹配,您的ak配置过程中安全码设置有问题,请确保:sha1正确,“;”分号是英文状态;且包名是您当前运行应用的包名,请按照说明文档重新申请KEY。
* 501~700:key验证失败,请按照说明文档重新申请KEY。
* 定位结果回调,重写onReceiveLocation方法,可以直接拷贝如下代码到自己工程中修改
*
*/
private BDLocationListener mListener = new BDLocationListener() {
@Override
public void onReceiveLocation(BDLocation location) {
mLocation = location;
if (location.getLocType() == BDLocation.TypeServerError
|| location.getLocType() == BDLocation.TypeNetWorkException
|| location.getLocType() == BDLocation.TypeCriteriaException) {
locationFailResult(location);
}else{
locationSuccessResult(location);
}
}
};
}