Android6.0的改变:
http://developer.android.com/intl/zh-cn/about/versions/marshmallow/android-6.0-changes.html
Android6.0 API改变:
http://developer.android.com/intl/zh-cn/about/versions/marshmallow/android-6.0.html
讲述了6.0的变化,api的改变,方法的改变,兼容说明等。
然而对开发人员而言,最主要的就是要兼容 Android 6.0 运行时权限 :Runtime Permissions。因为你会发现当你的targetSdkVersion=23的时候程序会莫名的闪退。
比如:启动摄像头,获取联系人信息,电话短信相关,日历相关,程序都会闪退。
运行时权限:就是当你的操作是有关用户隐私的时候,就会弹出一个授权Dialog,告诉用户APP要使用该权限,然后用户授权选择允许或者拒绝。
Android中哪些是正常权限,哪些是要用户授权的权限呢?
Normal and Dangerous Permissions
Normal Permission: 用户自动授权,直接在清单文件里写就Ok
As of API level 23, the following permissions are classified as PROTECTION_NORMAL
:
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_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
Dangerous Permissions:需要用户授权的权限
然而Dangerous permissions 被android 系统分成了权限组。
Dangerous permissions and permission groups 表格.
Permission Group | Permissions |
---|---|
CALENDAR | |
CAMERA | |
CONTACTS | |
LOCATION | |
MICROPHONE | |
PHONE | |
SENSORS | |
SMS | |
STORAGE |
备注:
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
Also starting in API level 19, this permission is not required to read/write files in your application-specific directories returned by getExternalFilesDir(String) and getExternalCacheDir().
从android-19开始,getExternalFilesDir(String) and getExternalCacheDir()不再需要权限。
getExternalFilesDir(String): /Android/data/包名/files/xxx
getExternalCacheDir(): /Android/data/包名/cache
重点:聊完常规权限和需要用户授权的权限,接下来看看如何声明权限,请求权限,处理请求结果等操作。
1,声明权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.snazzyapp">
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission-sdk-23 android:name="android.permission.READ_CONTACTS" />
<application ...>
...
</application>
</manifest>
2,检测权限和请求用户授权
Check For Permissions:
2种情况:1,Android6.0新的api。2,v4兼容api。
新的API:
activity.checkSelfPermission(permission)
v4兼容API:
ActivityCompat.checkSelfPermission(context, permission)
ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS)
ActivityCompat 和ContextCompat 是继承关系:
public class ActivityCompat extends ContextCompat {}
Request Permissions: 请求用户授权
新的API:
activity.requestPermissions (String[] permissions, int requestCode)
fragment.requestPermissions (String[] permissions, int requestCode)
v4兼容API:
ActivityCompat.requestPermissions(activity, permissions, requestCode);
并不是每次操作都要请求用户授权,so
Request the permissions you need 请求授权之前做逻辑判断
// checkSelfPermission 权限是否已经被允许
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) { // 没有授权
//之前被拒绝,弹窗说明,你用这个权限干嘛。
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} 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.
}
}else{//权限已经被允许
}
Handle the permissions request response :处理请求授权
//requestCode:用户请求授权的时候传递的参数
//permissions:权限列表
@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;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
完整实例代码:xml界面就一个简单的button
package com.heaven.android6;
public class MainActivity extends Activity implements OnClickListener{
private Button btn_rpermission;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_rpermission=(Button)findViewById(R.id.btn_rpermission);
btn_rpermission.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_rpermission:
// 如果运行6.0,但是没有做权限处理,直接调用对应的api,程序直接闪退。
// 权限逻辑处理。 如果清单文件没有配置权限,而去请求的话,程序直接闪退(没有配置一个权限的情况下)
try {
// checkSelfPermission 权限是否已经被允许
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// 没有被允许
System.out.println("没有被允许");
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_CONTACTS)) {
// 被拒绝,弹窗说明
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
System.out.println("被拒绝,弹窗说明 11111111111111111");
Toast.makeText(this, "被拒绝,弹窗提示,让用户自己设置", 0).show();
} else { // 清单文件已经配置了权限,第一次请求权限
System.out.println("请求权限2222222222222222");
// No explanation needed, we can request the permission.
Toast.makeText(this, "请求权限", 0).show();
// 2种方式 1,6.0api。2,v4兼容api
/*requestPermissions(
new String[]{Manifest.permission.READ_CONTACTS},
2);*/
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS},
2);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}else{//权限已经被允许
System.out.println("权限已经被允许33333333");
Toast.makeText(this, "权限已经被允许", 0).show();
return;
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
System.out.println("99999999999999999");
if(grantResults.length > 0){
int length = grantResults.length;
for (int i = 0; i < grantResults.length; i++) {
System.out.println("grantResults = "+ grantResults[i]);
}
}
switch (requestCode) {
case 2: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("准许获取联系人信息");
// permission was granted, yay! Do the
// contacts-related task you need to do.
Toast.makeText(this, "准许获取联系人信息", 0).show();
} else {
System.out.println("被拒绝");
Toast.makeText(this, "被拒绝,弹窗提示,让用户自己设置", 0).show();
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
}
更新sdk,下载samples,运行时权限demo位置:
D:\android-sdk-windows\samples\android-23\system