Android6.0相比以前的Android版本有一个很大的不一样点,就是动态的获取权限。以前咱们须要什么权限只须要在Manifest文件中声明便可,在6.0中,又新增了运行时权限的动态检测。
Android6.0分了两种权限Normal Permissions(安装时自动受权,用户也不能取消权限) and Dangerous Permissions(详情在文章最后):java
若是在 Android 6.0 之前的设备上,咱们以前在清单文件中声明权限是没有问题的,可是若是是在 Android 6.0 设备上,而且项目targetSdkVersion
你设置的是23,那再像以前那样声明权限,是不起做用的android
此时你确定想到了 若是 targetSdkVersion 值设置的小于23是否是就不会奔溃了,恩,确实如此, 此时即便使用Android6.0的设备,程序也不会奔溃,缘由显而易见,Android 的权限机制是 Android M 后才加入的。从 Android M 开始 应用程序申请的权限是在运行时动态赋给用户的。因此就须要咱们动态的申请权限了git
之因此写这篇文章是由于最近在写一个APP的时候用到了百度定位,以前使用百度定位的SDK好好的,可是换了手机以后开始不行了,折腾半天,才意识到新手机是6.0的系统,搜索一番,有所了解,写下来,权做备份
一个简单的例子,实现点击按钮,屏幕出现当前位置
申请密钥,配置环境就按照官网上面的来,咱们按照以前的方法来web
package cn.lixyz.testpermission;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.location.Poi;
import java.util.List;
public class MainActivity extends Activity /*implements BDLocationListener*/ {
private Button bt;
private TextView tv;
public LocationClient mLocationClient = null;
public BDLocationListener myListener = new MyLocationListener();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLocationClient = new LocationClient(getApplicationContext()); // 声明LocationClient类
mLocationClient.registerLocationListener(myListener); // 注册监听函数
initLocation();
bt = (Button) findViewById(R.id.bt);
tv = (TextView) findViewById(R.id.tv);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mLocationClient.start();
}
});
}
private void initLocation() {
LocationClientOption option = new LocationClientOption();
option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);// 可选,默认高精度,设置定位模式,高精度,低功耗,仅设备
option.setCoorType("bd09ll");// 可选,默认gcj02,设置返回的定位结果坐标系
int span = 1000;
option.setScanSpan(span);// 可选,默认0,即仅定位一次,设置发起定位请求的间隔须要大于等于1000ms才是有效的
option.setIsNeedAddress(true);// 可选,设置是否须要地址信息,默认不须要
option.setOpenGps(true);// 可选,默认false,设置是否使用gps
option.setLocationNotify(true);// 可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果
option.setIsNeedLocationDescribe(true);// 可选,默认false,设置是否须要位置语义化结果,能够在BDLocation.getLocationDescribe里获得,结果相似于“在北京天安门附近”
option.setIsNeedLocationPoiList(true);// 可选,默认false,设置是否须要POI结果,能够在BDLocation.getPoiList里获得
option.setIgnoreKillProcess(false);// 可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死
option.SetIgnoreCacheException(false);// 可选,默认false,设置是否收集CRASH信息,默认收集
option.setEnableSimulateGps(false);// 可选,默认false,设置是否须要过滤gps仿真结果,默认须要
mLocationClient.setLocOption(option);
}
class MyLocationListener implements BDLocationListener {
@Override
public void onReceiveLocation(BDLocation location) {
// Receive Location
StringBuffer sb = new StringBuffer(256);
sb.append("time : ");
sb.append(location.getTime());
sb.append("\nerror code : ");
sb.append(location.getLocType());
sb.append("\nlatitude : ");
sb.append(location.getLatitude());
sb.append("\nlontitude : ");
sb.append(location.getLongitude());
sb.append("\nradius : ");
sb.append(location.getRadius());
if (location.getLocType() == BDLocation.TypeGpsLocation) {// GPS定位结果
sb.append("\nspeed : ");
sb.append(location.getSpeed());// 单位:千米每小时
sb.append("\nsatellite : ");
sb.append(location.getSatelliteNumber());
sb.append("\nheight : ");
sb.append(location.getAltitude());// 单位:米
sb.append("\ndirection : ");
sb.append(location.getDirection());// 单位度
sb.append("\naddr : ");
sb.append(location.getAddrStr());
sb.append("\ndescribe : ");
sb.append("gps定位成功");
} else if (location.getLocType() == BDLocation.TypeNetWorkLocation) {// 网络定位结果
sb.append("\naddr : ");
sb.append(location.getAddrStr());
// 运营商信息
sb.append("\noperationers : ");
sb.append(location.getOperators());
sb.append("\ndescribe : ");
sb.append("网络定位成功");
} else if (location.getLocType() == BDLocation.TypeOffLineLocation) {// 离线定位结果
sb.append("\ndescribe : ");
sb.append("离线定位成功,离线定位结果也是有效的");
} else if (location.getLocType() == BDLocation.TypeServerError) {
sb.append("\ndescribe : ");
sb.append("服务端网络定位失败,能够反馈IMEI号和大致定位时间到loc-bugs@baidu.com,会有人追查缘由");
} else if (location.getLocType() == BDLocation.TypeNetWorkException) {
sb.append("\ndescribe : ");
sb.append("网络不一样致使定位失败,请检查网络是否通畅");
} else if (location.getLocType() == BDLocation.TypeCriteriaException) {
sb.append("\ndescribe : ");
sb.append("没法获取有效定位依据致使定位失败,通常是因为手机的缘由,处于飞行模式下通常会形成这种结果,能够试着重启手机");
}
sb.append("\nlocationdescribe : ");
sb.append(location.getLocationDescribe());// 位置语义化信息
List<Poi> list = location.getPoiList();// POI数据
if (list != null) {
sb.append("\npoilist size = : ");
sb.append(list.size());
for (Poi p : list) {
sb.append("\npoi= : ");
sb.append(p.getId() + " " + p.getName() + " " + p.getRank());
}
}
if (location.getCity() != null) {
Message msg = new Message();
msg.obj = location.getCity();
handler.sendMessage(msg);
}
}
}
Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
tv.setText((String) msg.obj);
mLocationClient.stop();
}
};
}
咱们点击按钮,发现并无定位成功,查看log,发现以下log网络
04-24 02:16:59.994 5329-5354/? W/System.err: java.lang.SecurityException: getAllCellInfo: Neither user 10198 nor current process has android.permission.ACCESS_COARSE_LOCATION. 04-24 02:16:59.994 5329-5354/? W/System.err: at android.app.ContextImpl.enforce(ContextImpl.java:1595) 04-24 02:16:59.994 5329-5354/? W/System.err: at android.app.ContextImpl.enforceCallingOrSelfPermission(ContextImpl.java:1627) 04-24 02:16:59.994 5329-5354/? W/System.err: at android.content.ContextWrapper.enforceCallingOrSelfPermission(ContextWrapper.java:675) 04-24 02:16:59.994 5329-5354/? W/System.err: at android.content.ContextWrapper.enforceCallingOrSelfPermission(ContextWrapper.java:675) 04-24 02:16:59.994 5329-5354/? W/System.err: at com.android.phone.PhoneInterfaceManager.enforceFineOrCoarseLocationPermission(PhoneInterfaceManager.java:1835) 04-24 02:16:59.994 5329-5354/? W/System.err: at com.android.phone.PhoneInterfaceManager.getAllCellInfoUsingSubId(PhoneInterfaceManager.java:1920) 04-24 02:16:59.994 5329-5354/? W/System.err: at com.android.phone.PhoneInterfaceManager.getAllCellInfo(PhoneInterfaceManager.java:1916) 04-24 02:16:59.994 5329-5354/? W/System.err: at com.android.internal.telephony.ITelephony$Stub.onTransact(ITelephony.java:732) 04-24 02:16:59.994 5329-5354/? W/System.err: at android.os.Binder.execTransact(Binder.java:453)
须要android.permission.ACCESS_COARSE_LOCATION
权限,咱们在Manifest文件中添加以后再运行,发现仍是抛一样的异常,依旧获取不到位置
因此咱们就须要针对6.0系统作出修改,来动态申请权限了。
修改代码:app
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startLocation();
}
});
private void startLocation() {
int checkPermission = ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION);
if (checkPermission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
Log.d("TTTT", "弹出提示");
return;
} else {
mLocationClient.start();
}
}
从新运行,点击按钮,发现弹出提示:
而且Log提示ide
04-24 02:29:16.163 6134-6134/cn.lixyz.testpermission D/TTTT: 弹出提示
咱们点击“始终容许”以后,退出程序再此进入,则能够定位了,咱们若是咱们像要在运行了权限以后当即就能够获取到位置信息呢?
Android提供了onRequestPermissionsResult
方法来帮咱们实现咱们执行了权限规则以后的操做,这个方法和onActivityResult
方法相似函数
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mLocationClient.start();
} else {
Log.d("TTTT", "啊偶,被拒绝了,少年不哭,站起来撸");
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
当咱们点击禁止按钮的时候,Log提示"啊偶,被拒绝了,少年不哭,站起来撸"
当咱们点击容许按钮的时候,定位成功
完整代码以下ui
public class MainActivity extends Activity{
private Button bt;
private TextView tv;
public LocationClient mLocationClient = null;
public BDLocationListener myListener = new MyLocationListener();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLocationClient = new LocationClient(getApplicationContext()); // 声明LocationClient类
mLocationClient.registerLocationListener(myListener); // 注册监听函数
initLocation();
bt = (Button) findViewById(R.id.bt);
tv = (TextView) findViewById(R.id.tv);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//mLocationClient.start();
startLocation();
}
});
}
private void startLocation() {
if(Build.VERSION.SDK_INT>=23){
int checkPermission = ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION);
if (checkPermission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
return;
} else {
mLocationClient.start();
}
}else{
mLocationClient.start();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mLocationClient.start();
} else {
Log.d("TTTT", "啊偶,被拒绝了,少年不哭,站起来撸");
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
private void initLocation() {
LocationClientOption option = new LocationClientOption();
option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);// 可选,默认高精度,设置定位模式,高精度,低功耗,仅设备
option.setCoorType("bd09ll");// 可选,默认gcj02,设置返回的定位结果坐标系
int span = 1000;
option.setScanSpan(span);// 可选,默认0,即仅定位一次,设置发起定位请求的间隔须要大于等于1000ms才是有效的
option.setIsNeedAddress(true);// 可选,设置是否须要地址信息,默认不须要
option.setOpenGps(true);// 可选,默认false,设置是否使用gps
option.setLocationNotify(true);// 可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果
option.setIsNeedLocationDescribe(true);// 可选,默认false,设置是否须要位置语义化结果,能够在BDLocation.getLocationDescribe里获得,结果相似于“在北京天安门附近”
option.setIsNeedLocationPoiList(true);// 可选,默认false,设置是否须要POI结果,能够在BDLocation.getPoiList里获得
option.setIgnoreKillProcess(false);// 可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死
option.SetIgnoreCacheException(false);// 可选,默认false,设置是否收集CRASH信息,默认收集
option.setEnableSimulateGps(false);// 可选,默认false,设置是否须要过滤gps仿真结果,默认须要
mLocationClient.setLocOption(option);
}
class MyLocationListener implements BDLocationListener {
@Override
public void onReceiveLocation(BDLocation location) {
// Receive Location
StringBuffer sb = new StringBuffer(256);
sb.append("time : ");
sb.append(location.getTime());
sb.append("\nerror code : ");
sb.append(location.getLocType());
sb.append("\nlatitude : ");
sb.append(location.getLatitude());
sb.append("\nlontitude : ");
sb.append(location.getLongitude());
sb.append("\nradius : ");
sb.append(location.getRadius());
if (location.getLocType() == BDLocation.TypeGpsLocation) {// GPS定位结果
sb.append("\nspeed : ");
sb.append(location.getSpeed());// 单位:千米每小时
sb.append("\nsatellite : ");
sb.append(location.getSatelliteNumber());
sb.append("\nheight : ");
sb.append(location.getAltitude());// 单位:米
sb.append("\ndirection : ");
sb.append(location.getDirection());// 单位度
sb.append("\naddr : ");
sb.append(location.getAddrStr());
sb.append("\ndescribe : ");
sb.append("gps定位成功");
} else if (location.getLocType() == BDLocation.TypeNetWorkLocation) {// 网络定位结果
sb.append("\naddr : ");
sb.append(location.getAddrStr());
// 运营商信息
sb.append("\noperationers : ");
sb.append(location.getOperators());
sb.append("\ndescribe : ");
sb.append("网络定位成功");
} else if (location.getLocType() == BDLocation.TypeOffLineLocation) {// 离线定位结果
sb.append("\ndescribe : ");
sb.append("离线定位成功,离线定位结果也是有效的");
} else if (location.getLocType() == BDLocation.TypeServerError) {
sb.append("\ndescribe : ");
sb.append("服务端网络定位失败,能够反馈IMEI号和大致定位时间到loc-bugs@baidu.com,会有人追查缘由");
} else if (location.getLocType() == BDLocation.TypeNetWorkException) {
sb.append("\ndescribe : ");
sb.append("网络不一样致使定位失败,请检查网络是否通畅");
} else if (location.getLocType() == BDLocation.TypeCriteriaException) {
sb.append("\ndescribe : ");
sb.append("没法获取有效定位依据致使定位失败,通常是因为手机的缘由,处于飞行模式下通常会形成这种结果,能够试着重启手机");
}
sb.append("\nlocationdescribe : ");
sb.append(location.getLocationDescribe());// 位置语义化信息
List<Poi> list = location.getPoiList();// POI数据
if (list != null) {
sb.append("\npoilist size = : ");
sb.append(list.size());
for (Poi p : list) {
sb.append("\npoi= : ");
sb.append(p.getId() + " " + p.getName() + " " + p.getRank());
}
}
if (location.getCity() != null) {
Message msg = new Message();
msg.obj = location.getCity();
handler.sendMessage(msg);
}
}
}
Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
tv.setText((String) msg.obj);
mLocationClient.stop();
}
};
}
Normal Permissions
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
REQUEST_INSTALL_PACKAGESthis
Dangerous Permissions
Permission Group:CALENDAR
READ_CALENDAR
WRITE_CALENDAR
Permission Group:CAMERA
CAMERA
Permission Group:CONTACTS
READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
Permission Group:LOCATION
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
Permission Group:MICROPHONE
RECORD_AUDIO
Permission Group:PHONE
READ_PHONE_STATE
CALL_PHONE
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
Permission Group:SENSORS
BODY_SENSORS
Permission Group:SMS
SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
Permission Group:STORAGE
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE