视频为本篇博客知识的讲解,建议采用超清模式观看, 欢迎点击订阅我的优酷

上篇文章我们讲解了通过隐式意图拨打电话,在AndroidManifest.xml文件中添加了权限

<uses-permission android:name="android.permission.CALL_PHONE"/>

接下来我们单独写个拨打电话的代码,添加好权限,并部署到最新的Android 6.0的模拟器上,需要注意的小细节,我在app/build.gradle的配置为:

android {
compileSdkVersion 22
buildToolsVersion "23.0.1" defaultConfig {
applicationId "com.demo.myapplication"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}

需要注意的是 targetSdkVersion22 代表适配的版本是Android5.1 并不是Android6.0, 我们先来看看运行结果,当我们点击拨打电话,是可以正常运行的吗,并没有任何问题。

代码也是和之前的一模一样:

    public void callPhone(View view) {

        Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + 5335458));
startActivity(intent);
}

然而,这并不是重点,接下来重点来了,上面的代码目标版本是Android5.1,接下来我们把targetSdkVersion改成最新的23, 前提是你的sdk里面有23这个版本。

android {
compileSdkVersion 23
buildToolsVersion "23.0.1" defaultConfig {
applicationId "com.demo.myapplication"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}

我们把改后的代码重新装到23版本的模拟器中,这时候当我们点击拨打电话的时候,程序崩溃了,

我仅仅改了一处地方,带来的变化是如此之大。是不是有种想撞墙的感觉,我们先来看看Android6.0重要的变化。

Android6.0 新的权限机制

android在不断发展,android的权限系统一直是首要的安全概念,因为这些权限只在安装的时候被询问一次。一旦安装了,app可以在用户毫不知晓的情况下访问权限内的所有东西。(国内定制系统除外)

难怪一些坏蛋利用这个缺陷恶意收集用户数据用来做坏事了!

android小组也知道这事儿。权限系统终于被重新设计了。在android6.0棉花糖,app将不会在安装的时候授予权限。取而代之的是,app不得不在运行时一个一个询问用户授予权限。如下图(“你好”是程序的名字,大家别误会….):

注意权限询问对话框不会自己弹出来。开发者不得不自己调用。如果开发者要调用的一些函数需要某权限而用户又拒绝授权的话,函数将抛出异常直接导致程序崩溃。

另外,用户也可以随时在设置里取消已经授权的权限。

你或许已经感觉到背后生出一阵寒意。。。如果你是个android开发者,意味着要完全改变你的程序逻辑。你不能像以前那样直接调用方法了,你不得不为每个需要的地方检察权限,否则app就崩溃了!

是的。我不能哄你说这是简单的事儿。尽管这对用户来说是好事,但是对开发者来说就是噩梦。我们不得不修改编码不然不论短期还是长远来看都是潜在的问题。

这个新的运行时权限仅当我们设置targetSdkVersion to 23(这意味着你已经在23上测试通过了)才起作用,当然还要是6.0系统的手机。app在6.0之前的设备依然使用旧的权限系统。

这就可以解释上面的例子,如果你的代码targetSdkVersion是小于23的,Android系统默认采取低版本的权限规则,如果targetSdkVersion=23,系统就会认为你的程序是经历过23版本测试的,就会采用新的权限管理机制.所以如果大家的程序没有适配好6.0,不要轻易改成targetSdkVersion=23。

之前的App如果没有兼容6.0,也是可以在6.0系统上嗷嗷的跑,不会有问题,但是用户是可以关闭授权的,这会影响用户的正常体验。我们如何在6.0上申请权限呢?

申请权限

权限分为两种:

第一种就是危害不大的,比如手机振动权限,这种权限是用户在安装程序的时候添加的,和之前请求权限是一样的,没有任何变化,就是在清单文件中添加相关的权限,这些权限我简单的列了出来:

android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_NOTIFICATION_POLICY
android.permission.ACCESS_WIFI_STATE
android.permission.ACCESS_WIMAX_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE_NETWORK_STATE
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.CHANGE_WIMAX_STATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND_STATUS_BAR
android.permission.FLASHLIGHT
android.permission.GET_ACCOUNTS
android.permission.GET_PACKAGE_SIZE
android.permission.INTERNET
android.permission.KILL_BACKGROUND_PROCESSES
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST_INSTALL_PACKAGES
android.permission.SET_TIME_ZONE
android.permission.SET_WALLPAPER
android.permission.SET_WALLPAPER_HINTS
android.permission.SUBSCRIBED_FEEDS_READ
android.permission.TRANSMIT_IR
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SYNC_SETTINGS
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
com.android.launcher.permission.UNINSTALL_SHORTCUT

第二种权限就是涉及到用户隐私之类的权限,这类权限就需要在代码中动态请求用户批准了,权限被分组了,如下表:



同一组的任何一个权限被授权了,其他权限也自动被授权。例如,一旦WRITE_CONTACTS被授权了,app也有READ_CONTACTS和GET_ACCOUNTS了。

源码中被用来检查和请求权限的方法分别是Activity的checkSelfPermission和requestPermissions。这些方法api23引入。

我们接下来把上面的例子进行修改:

    public void callPhone(View view) {
// 检查是否有授权
int i = checkSelfPermission(Manifest.permission.CALL_PHONE);
if(i!= PackageManager.PERMISSION_GRANTED){ // 当没有授权的时候调用
// 参数1请求的授权,可以同时请求多个授权, 参数2为请求码
requestPermissions(new String[]{Manifest.permission.CALL_PHONE},1);
return;
}
// 如果有授权直接拨打电话
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + 5335458));
startActivity(intent);
}

当调用requestPermissions()方法的时候,用户就会看到请求授权的对话框,当用户点击对话框的时候就会调用Activity的onRequestPermissionsResult方法,我们接下来在这个方法中去实现拨打电话:

    @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if(requestCode==1){
if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
// 权限通过
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + 5335458));
startActivity(intent);
}else{
// 权限拒绝
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults); }

上面的代码有一个问题,就是当程序部署到6.0以下的设备的时候,就会报错,因为之前版本没有checkSelfPermission和requestPermissions方法,粗暴的方法就是直接检查版本,

if (Build.VERSION.SDK_INT >= 23) {
// 6.0之后的操作
} else {
// 6.0之前的操作
}

其实我们可以借助v7包里的api,

ContextCompat.checkSelfPermission()

被授权函数返回PERMISSION_GRANTED,否则返回PERMISSION_DENIED ,在所有版本都是如此。

ActivityCompat.requestPermissions()

这个方法在6.0之前版本调用,OnRequestPermissionsResultCallback 直接被调用,带着正确的 PERMISSION_GRANTED或者 PERMISSION_DENIED 。

ActivityCompat.shouldShowRequestPermissionRationale()

在6.0之前版本调用,永远返回false。

用v7包的这三方法,完美兼容所有版本!这个方法需要额外的参数,Context or Activity。别的就没啥特别的了。下面是完整代码:

public class Main2Activity extends AppCompatActivity {

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
} public void callPhone(View view) {
int i = ContextCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE);
if(i!= PackageManager.PERMISSION_GRANTED){ // 当没有授权的时候调用
// 参数1申请的权限,可以同时请求多个授权, 参数2请求码
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CALL_PHONE},1);
return;
}
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + 5335458));
startActivity(intent);
} @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if(requestCode==1){
if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
// 权限通过
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + 5335458));
startActivity(intent);
}else{
// 权限拒绝
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults); }
}

我们也可以在Fragment中使用,用v13兼容包:FragmentCompat.requestPermissions(), FragmentCompat.shouldShowRequestPermissionRationale().和activity效果一样。

最后的建议

我相信你对新权限模型已经有了清晰的认识。我相信你也意识到了问题的严峻。

如果你代码没支持新权限,不要设置targetSdkVersion 23 。尤其是当你在Android Studio新建工程时,不要忘了修改!

如果你有要维护的程序,建议你提前重构好代码,否则,一旦加上新的授权认证,代码很难保证像以前容易阅读。

Android教程 -05 Android6.0权限的管理的更多相关文章

  1. Android6.0权限大全和权限分类

    本文转载至: https://blog.csdn.net/qq_26440221/article/details/53097868 自从出了Android6.0权限管理之后,再也不能像以前那样粘贴复制 ...

  2. Android6.0权限管理以及使用权限该注意的地方

    Android 6.0 Marshmallow首次增加了执行时权限管理,这对用户来说,能够更好的了解.控 制 app 涉及到的权限.然而对开发人员来说却是一件比較蛋疼的事情.须要兼容适配,并保证程序功 ...

  3. android6.0权限管理工具EasyPermissionUtil

    前言 android6.0開始,权限的申请发生了改变,申请变的动态化,也就是执行时权限,和iOS相仿,动态化的意思是指,在每次使用须要危急权限的方法的时候.须要检查程序是否获得了该权限的许可.动态化的 ...

  4. 【Android开发】Android6.0请求权限方式

    记录一下最普通的动态请求权限的方法: private int requestCode == 123; //判断当前系统的版本 if(Build.VERSION.SDK_INT >= 23){ i ...

  5. Android6.0权限组申请

    void checkPermission() { final List<String> permissionsList = new ArrayList<>(); if (Bui ...

  6. Android权限管理PermissionsDispatcher2.3.2使用+原生6.0权限使用

    PermissionsDispatcher2.3.2使用 Android6.0权限官网https://developer.android.com/about/versions/marshmallow/ ...

  7. Android开发——Android 6.0权限管理机制详解

    .Android 6.0运行时主动请求权限 3.1  检测和申请权限 下面的例子介绍上面列出的读写SD卡的使用例子,可以使用以下的方式解决: public boolean isGrantExterna ...

  8. Android6.0动态权限申请步骤以及需要注意的一些坑

    因为工作需要,简单研究了一下Android6.0权限申请,在Google提供的sample的基础上,写了一个简单的demo.算是自己的笔记吧,可能会比较混乱,主要是方便以后查看.后期有别的问题,随时更 ...

  9. android6.0、7.0、8.0新特性总结之开发应用时加以考虑的一些主要变更。

    android6.0 参考一:简书Android 6.0 新特性详解 参考二:关于Android6.0以上系统的权限问题 参考三:值得你关注的Android6.0上的重要变化(一) 参考四:值得你关注 ...

随机推荐

  1. 高可用服务 AHAS 在消息队列 MQ 削峰填谷场景下的应用

    在消息队列中,当消费者去消费消息的时候,无论是通过 pull 的方式还是 push 的方式,都可能会出现大批量的消息突刺.如果此时要处理所有消息,很可能会导致系统负载过高,影响稳定性.但其实可能后面几 ...

  2. Statement对象

    Statement 对象 创建 Statement 对象 在你准备使用 Statement 对象执行 SQL 语句之前,你需要使用 Connection 对象的 createStatement() 方 ...

  3. Linux 配置yum源(互联网)

    Linux 配置yum源(互联网) 环境:操作系统Redhat 7.5 1.卸载现有的yum源 rpm -qa|grep yum|xargs rpm -e --nodeps     #移除与原yum有 ...

  4. vue组件通信全面总结

    写在前面 组件间的通信是是实际开发中非常常用的一环,如何使用对项目整体设计.开发.规范都有很实际的的作用,我在项目开发中对此深有体会,总结下vue组件间通信的几种方式,讨论下各自的使用场景 文章对相关 ...

  5. KiCad 元件值 F4NNIU 规范(2019-10-10)

    目录 KiCad 元件值 F4NNIU 规范 电阻 电容 电感 二极管 三极管 MOSFET IC 文件历史 KiCad 元件值 F4NNIU 规范 为方便物料统计以及规范物料,在制造 BOM 时可以 ...

  6. JQuery--基础动画、滑动动画、淡入淡出动画、自定义动画

    /** * [JQ基础动画] * show() 显示 * hide() 隐藏 * toggle() 切换 * 默认无动画,如果要产生动画 * 在括号内,添加毫秒数,可产生动画和控制动画的快慢 * * ...

  7. 数据库lib7第2, 3题(创建索引和触发器)

    2. 分别为上述建立的表格建立适当的索引,请描述建立索引的过程(可以截图或者写SQL).其中,要求对SPJ标中的SNo, PNo字段各建立一个索引,为(PNo, JNo)的组合建立一个索引.请问,SN ...

  8. Mac查看Python安装路径和版本

    目录 #查看当前所有Python版本路径 appledeMBP:~ apple$ which python2.7 /usr/local/bin/python2.7 appledeMBP:~ apple ...

  9. 从零学React Native之12 组件的生命周期

    一个React Native组件从它被加载,到最终被卸载会经历一个完整的生命周期.所谓生命周期,就是一个对象从开始生成到最后消亡所经历的状态,理解生命周期,是合理开发的关键. ES6语法和之前的ES5 ...

  10. 【JZOJ4869】【NOIP2016提高A组集训第9场11.7】平均数

    题目描述 数据范围 解法 二分答案. 对于一个答案mid,要求出区间平均数小于mid的个数ans. 给所有数减去mid,那么问题转化为求出所有区间和为负数的个数. 对于一个区间[l,r],如果sum[ ...