运行时权限
此版本引入了一种新的权限模式,如今,用户可直接在运行时管理应用权限。这种模式让用户能够更好地了解和控制权限,同时为应用开发者精简了安装和自动更新过程。用户可为所安装的各个应用分别授予或撤销权限。
对于以 Android 6.0(API 级别 23)或更高版本为目标平台的应用,请务必在运行时检查和请求权限。要确定您的应用是否已被授予权限,请调用新增的 checkSelfPermission() 方法。要请求权限,请调用新增的 requestPermissions() 方法。即使您的应用并不以 Android 6.0(API 级别 23)为目标平台,您也应该在新权限模式下测试您的应用。
如需了解有关在您的应用中支持新权限模式的详情,请参阅使用系统权限。如需了解有关如何评估新模式对应用的影响的提示,请参阅权限最佳做法。
新增检查方法 checkSelfPermission()
| 参数 |
permission String:正在检查的权限的名称。
这个值绝对不能null。
| 返回 |
| int | PackageManager.PERMISSION_GRANTED如果您有权限,或者如果没有。PackageManager.PERMISSION_DENIED
价值是PERMISSION_GRANTED或PERMISSION_DENIED。
新增请求权限方法 requestPermissions()
| 参数 |
permissions String:请求的权限。我必须非空并且不是空的。
requestCode int:特定于应用程序的请求代码以与报告的结果匹配。应该>=0onRequestPermissionsResult(int, String[], int[])
| 抛出 |
IllegalArgumentException 如果requestCode为负数。
官方有个地方说可以使用ContextWrapper.checkSelfPermission(String) 。其实这个方法很多地方都能使用到。
好,Android M 为什么会新增这两方法呢.因为在Android 6.0 (API 23) 开始用户开始在应用运行时向其授予权限,而不是在应用安装时授予。这种权限机制可以让用户更好的管理应用的权限,保障用户隐私。从此之后...
系统权限分为两种
正常权限
不会直接给用户隐私权带来风险。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。
android.permission.ACCESS LOCATIONEXTRA_COMMANDS
android.permission.ACCESS NETWORKSTATE
android.permission.ACCESS NOTIFICATIONPOLICY
android.permission.ACCESS WIFISTATE
android.permission.ACCESS WIMAXSTATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE NETWORKSTATE
android.permission.CHANGE WIFIMULTICAST_STATE
android.permission.CHANGE WIFISTATE
android.permission.CHANGE WIMAXSTATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND STATUSBAR
android.permission.FLASHLIGHT
android.permission.GET_ACCOUNTS
android.permission.GET PACKAGESIZE
android.permission.INTERNET
android.permission.KILL BACKGROUNDPROCESSES
android.permission.MODIFY AUDIOSETTINGS
android.permission.NFC
android.permission.READ SYNCSETTINGS
android.permission.READ SYNCSTATS
android.permission.RECEIVE BOOTCOMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST INSTALLPACKAGES
android.permission.SET TIMEZONE
android.permission.SET_WALLPAPER
android.permission.SET WALLPAPERHINTS
android.permission.SUBSCRIBED FEEDSREAD
android.permission.TRANSMIT_IR
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE SYNCSETTINGS
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
com.android.launcher.permission.UNINSTALL_SHORTCUT
危险权限
会授予应用访问用户机密数据的权限。如果您列出了危险权限,则用户必须明确批准您的应用使用这些权限。
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
阅读到这里,小伙伴会有疑问.为什么危险权限一组一组。这里解释一下,比如 permission-group.SMS 中的其中一条READ_SMS被授权了。那么在使用其他同组权限的时候就不会去向用户发起请求,我们使用checkSelfPermission得到的结果也是授权的。
兼容性的问题
在Android 6.0 以前只要在AndroidManifest.xml上申明了权限,应用就默认带有权限.(在原生系统中)。有些厂商的手机一样可以让用户关闭掉权限。那么...在API<23的情况下关掉权限.再使用权限.那么只有死路条.所以。采取良好的方案就是Try 。
封装思路
其实每写一篇简书前我都会看其他人写的文章,这样就可以把每个人的优势结合在一起。我之前做适配的时候也在Github上找过动态权限申请的库,也可以说成是工具类把。我去看了源码。又一个声称可以在任何地方都可以使用到权限,后来我就用上了这个。其实当时我并不是很理解,因为权限当时我去找资料了是Activity才能发起了.回调固然回到Activity,后来我翻看他的源码...是new 了一个Activity出来.还有一些工具类是让你一行代码调用.然后在onRequestPermissionsResult也要写一行工具类的代码.我觉得这种比较麻烦。后来我就想到了。将使用到的方法功能封装在一个Activity里面。让BaseActivity去继承这个PermissionRequestActivity。一个方法使用,给一个回调出来,授权权限是否成功。
PermissionRequestActivity
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.AlertDialog;
/**
* Created by 卖火柴的小女孩 - Jc on 2018/8/21.
*/
public class PermissionRequestActivity extends FragmentActivity {
private static final int PERMISSION_REQUEST_CODE = 1088;
private String mPermissionDes;
private CallBack mCallBack;
/**
* 权限申请使用方法
*
* @param permissionDes 权限说明
* @param callBack 申请回调
* @param permissions 申请权限
*/
protected void requestPermission(String permissionDes, CallBack callBack, @NonNull String... permissions) {
mCallBack = callBack;
mPermissionDes = permissionDes;
if (checkPermission(permissions))
mCallBack.hasPermission();
else {
ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
}
}
/**
* 判断系统版本大于6.0的时候
*
* @param permissions 申请权限
* @return
*/
protected boolean checkPermission(@NonNull String... permissions) {
//大于6.0的时候需要动态申请权限.小于6.0的时候如果用户手动关闭权限,程序即崩 需要做Try处理
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return checkSelfPermissions(permissions);
}
return true;
}
/**
* 检查权限是否已经授权
*
* @param permissions 申请权限
* @return
*/
private boolean checkSelfPermissions(@NonNull String... permissions) {
boolean flag = true;
for (String p : permissions) {
if (ActivityCompat.checkSelfPermission(this, p) != PackageManager.PERMISSION_GRANTED) {
flag = false;
break;
}
}
return flag;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
boolean hasAllGranted = true;
for (int i = 0; i < grantResults.length; ++i) {
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
hasAllGranted = false;
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) {
showDialogPrompt();
} else {
//权限申请被拒绝 ,但用户未选择'不再提示'选项
mCallBack.lossPermission();
}
break;
}
}
if (hasAllGranted) {
mCallBack.hasPermission();
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
/**
* 由于用户手动关闭权限提示。APP需要做人性化提示
*/
private void showDialogPrompt() {
new AlertDialog.Builder(this)
.setTitle("权限申请")
.setMessage(mPermissionDes)
.setCancelable(false)
.setPositiveButton("去设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//引导用户至设置页手动授权
getAppSetting();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//引导用户手动授权,权限请求失败
mCallBack.lossPermission();
}
}).show();
}
/**
* 跳转设置 应用设置界面
*
* @return
*/
private Intent getAppSetting() {
Intent localIntent = null;
if (Build.VERSION.SDK_INT >= 9) {
localIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
localIntent.setData(Uri.fromParts("package", getPackageName(), null));
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} else if (Build.VERSION.SDK_INT <= 8) {
localIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
localIntent.setAction(Intent.ACTION_VIEW);
localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
localIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
startActivity(localIntent);
return localIntent;
}
/**
* 权限申请回调
*/
interface CallBack {
void hasPermission();
void lossPermission();
}
}
使用
requestPermission("获取手机信息-获取手机号码、IMEI、IMSI权限\n读写手机存储-读写手机存储", new CallBack() {
@Override
public void hasPermission() {
String IMEI = getIMEI(MainActivity.this);
((TextView) findViewById(R.id.tv)).setText(IMEI);
}
@Override
public void lossPermission() {
Toast.makeText(MainActivity.this, "权限申请被拒绝", Toast.LENGTH_SHORT).show();
}
}, Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_EXTERNAL_STORAGE);
代码讲解
其实代码不多,只有100多行。
方法: requestPermission
参数:String permissionDes -- 用于用户选择'不再提示'按钮提示语
CallBack callBack -- 应用是否带有权限或是否授权权限成功
@NonNull String... permissions -- 需要授权或检查的权限
这个方法一进去 我做了一个判断 checkPermission(permissions) , 先去检查是否有权限,如果有权限的话.那么就不去授权权限了.
方法 :checkPermission() 进来判断是否有权限的时候这个方法有一个API 就是刚才最上面开始讲的checkSelfPermission 这个需要 Build.VERSION.SDK_INT >= Build.VERSION_CODES.M..所以不满足这个条件的都以TRUE返回出去,表明应用在6.0以下的设备默认有这些权限。但是刚才在兼容性的时候讲过了.会崩。
接下来检测到没有权限,使用了这一段代码
ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
这一行就是向用户发起权限授权的代码了.接下来 我们看 onRequestPermissionsResult 。 这个方法是向用户请求权限的回调结果。这个方法里面我做了处理,我检测了用户是否点击了 ' 不再提示 ' 的按钮。(有些手机没有这个按钮,有些手机有,当选择了此按钮之后 再次发起授权请求,系统将不再向用户发起权限授权请求.而是直接返回拒绝的信息回来.)那么这时我们就需要做一个友好的提示了。如果用户是由于手动点击了这个不再提示的按钮而导致第二次或第n次没有收到授权的消息.那我们应用需要给用户一个提示框showDialogPrompt(),我这里写了一个最简单的弹窗。很多APP都有自己主题的弹窗.主要就是提示用户。告知用户 他的权限被他自己手动不再提示了.那么我们给他一个系统设置界面的跳转getAppSetting(); 然后这个方法的Build.VERSION.SDK_INT <= 8 我就不解释了.有兴趣的就自己去百度。
网友评论