现在的主流手机的Android版本都是8.0/9.0(笔者的华为手机为9.0),所以在开发Android的时候尽量使用Android8.0的版本(SDK 26)。随着Android版本的提升,系统的安全性得到了加强,但是同时增加了开发的难度。Android6.0以上请求应用权限就是一个例子。

笔者以获取读取外部储存为例,实现从系统相册获取图片的功能。在调用系统相册API的时候不需要读取外部储存的权限,但是将从系统相册获取的图片显示时需要。

我在实现调用系统相册时,使用了Android 调用系统相册选择图片并显示中的代码,在此感谢该博客作者。

经过简单的CTRL+C、CTRL+V后,我成功运行了代码,并且成功的调用了系统相册。

但是在选择图片并点击“✓”后,主屏幕没有出现任何变化,并没有像想象中的出现选择的图片。

然后我注意到该博客的作者在代码中备注了要加上这样一个获取权限的代码:

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

目的:获取外部储存的读取权限。

增加方法:在AndroidManifest.xml中的下图所示位置增加

但是即便这样,也无法将从相册获取的图片显示。在调试时,发现选择图片并点击“✓”后报出了这样的错误:

这里的错误信息说明找不到该图片,但是后面的“/storage/...”中正是该图片的路径。这告诉我问题不是出在相册上(相册能正确的获取图片的路径),而是出在应用的权限上。

解决问题的过程:

这时我回想起来一个我们用Android手机时经常遇到的情况。每当下载一个新的app时,它会在你安装的时候请求权限,但是你是否对下面的图片有印象:

既然能在安装时请求权限,那为什么还要在运行程序时请求一遍呢。而且很多应用在你拒绝某些权限后会直接闪退。

通过查找资料,我终于在博客一行代码搞定Android 6.0动态权限申请中找到了原因:在Android6.0后,为了提高安全性,应用在请求一些危险权限时需要在运行应用时请求一遍(大概是为了防止有人在安装应用时看都不看就安装),常见的危险权限就是使用储存、读取联系人等你在运行新软件时见到的。(常见的危险权限)

对于这些危险权限,我们仅仅在AndroidManifest.xml中增加权限是不管用的,我们需要在MainActivity中增加一些代码,以显示上图所示的UI。(不用担心,该UI是Android在请求权限时自动产生的,不需要用冗长的代码生成)

问题的解决:

1. 首先在AndroidManifest.xml中安照上面的方法添加你想要的权限

2. 当你请求的权限为危险权限时(如READ_EXTERNAL_STORAGE权限),需要知道以下知识:

(1)在应用运行时(通常在onCreate方法中),首先要得知程序是否获得了该危险权限,若未获取,才执行请求权限的代码(若不这样做的话,每次打开软件都向你请求一次权限,你觉得烦不烦?)

通过下面的代码即可得知是否已经获取READ_EXTERNAL_STORAGE权限:

int permissionCheck = ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.READ_EXTERNAL_STORAGE);

第一个参数MainActivity.this为该Activity,及你需要获取权限的Activity;

第二个参数Manifest.permission.READ_EXTERNAL_STORAGE与AndroidManifest.xml中一样,应该在“READ_EXTERNAL_STORAGE”填写你想要获得的权限名(与AndroidManifest.xml中一致)。

此处需要导入Manifest类(在代码的“Manifest”上按下ALT+ENTER)

根据返回到permissionCheck中的值可以判断是否已经获取权限,

若permissionCheck==PackageManager.PERMISSION_DENIED则未获取;

若permissionCheck==PackageManager.PERMISSION_GRANTED则已获取。

(2)若未获取权限,则执行下面的代码,以生成前面请求权限的UI

ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);

第一个参数MainActivity.this为该Activity,及你需要获取权限的Activity;

第二个参数为一个String数组,数组中可以存储多个请求的权限,及一次可以请求多个权限。由于我们只请求外部储存权限,所以创建的String数组只有一个元素Manifest.permission.READ_EXTERNAL_STORAGE,它与得知是否获取权限的参数是一样的,只是存储在了String数组中。

第三个参数MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE为一个自定义的int值对象,开始我以为这个参数是用来选择请求权限的方式的,后来我才知道这是用来与后面的回调函数onRequestPermissionsResult()进行“通信”的。我们需要在MainActivity中声明该属性的值,如final private int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1;,然后在下面的回调函数再使用这个值。

(3)当用户选择是/否赋予权限后,系统将自动调用MainActivity中的onRequestPermissionsResult()方法,以处理用户的响应(比如不同意就退出应用之类的)。

这个方法是系统已经声明的,你需要在MainActivity中重写该方法。具体操作是在MainActivity中按快捷键CTRL+O,在弹出的窗口中找到onRequestPermissionsResult(),点击确定即可生成模板。

Android官方的代码如下:

@Overridepublic 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.}}

代码中的“case MY_PERMISSIONS_REQUEST_READ_CONTACTS:”中的MY_PERMISSIONS_REQUEST_READ_CONTACTS即是上一步中的MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE,只是它请求的权限不同,所以跟我命的名不同。

其作用就是在有多次请求时,能够区分是哪个请求调用的onRequestPermissionsResult()方法,然后进行相应的操作。

一般的程序中只需将Android官方提供的模板中的“MY_PERMISSIONS_REQUEST_READ_CONTACTS”改为自己定义的int对象即可正常运行,感兴趣的同学可以研究下Android官方的注释。


将上面提到的博客Android 调用系统相册选择图片并显示中的MainActivity改成下面的代码,并在AndroidManifest.xml添加请求权限的代码!即可在Android6.0以上的手机运行了!

package com.example.systempicture;import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;import android.view.View;
import android.widget.ImageView;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;public class MainActivity extends AppCompatActivity {final private int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1;//用于判断请求的来源//调用系统相册-选择图片private static final int IMAGE = 1;//所需权限
//    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//使用相册前首先需要获取权限int permissionCheck = ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.READ_EXTERNAL_STORAGE);//如果有权限则返回PackageManager.PERMISSION_GRANTED,否则返回PackageManager。PERMISSION_DENIED。if(permissionCheck!= PackageManager.PERMISSION_GRANTED){//为获取权限时//请求获取权限ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);//用new String[]的原因是可以在String[]中存储多个需要的权限,一次过请求//将回调onRequestPermissionsResult()方法}}/*** "点击选择图片"的按钮的监听方法* @param v*/public void OnClick(View v) {//调用相册Intent intent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);startActivityForResult(intent, IMAGE);}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {switch (requestCode) {case MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE: {// 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;}}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);//获取图片路径if (requestCode == IMAGE && resultCode == Activity.RESULT_OK && data != null) {Uri selectedImage = data.getData();String[] filePathColumns = {MediaStore.Images.Media.DATA};Cursor c = getContentResolver().query(selectedImage, filePathColumns, null, null, null);c.moveToFirst();int columnIndex = c.getColumnIndex(filePathColumns[0]);String imagePath = c.getString(columnIndex);showImage(imagePath);c.close();}}//加载图片private void showImage(String imaePath){Bitmap bm = BitmapFactory.decodeFile(imaePath);((ImageView)findViewById(R.id.image)).setImageBitmap(bm);}}

请求权限窗口一闪而过?

在MainActivity中使用上面的代码,但是打开应用时没有看到请求权限的UI,仔细看可以发现其刚显示一下就立即消失了!

这是因为你没有在AndroidManifest.xml添加请求权限的代码!

对于危险权限,在AndroidManifest.xml和MainActivity中都要申请该权限哦!普通权限在AndroidManifest.xml就可以了。

[Android Studio]详细讲解Android6.0以上请求应用权限(解决请求权限窗口一闪而过的问题)相关推荐

  1. linux安装 Android Studio详细教程,支持性较差,需要安装最新底层库内核的linux

    安装 Android Studio详细教程 libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5 lib32z1 jdk1.8.0_25 android-st ...

  2. android动态获取地理位置权限,Android6.0获取GPS定位和获取位置权限和位置信息的方法...

    1.添加权限--6.0之后要动态获取,下面会说 2.直接上代码,不多说,代码中注释很详细. private static final int BAIDU_READ_PHONE_STATE = 100; ...

  3. Android架构详细讲解与C/C++开发支持原理

    Android架构详细讲解与C/C++开发支持原理 在Android 在NDK r5使用C/C++进行开发.(以前,Android 对C/C++开发的支持仅限于用C/C++开发动态链接库,然后在Jav ...

  4. Android Studio 升级到3.0后出现编译错误\.gradle\caches\transforms-1\files-1.1\***** 解决办法

    Android Studio 升级到3.0后出现各种编译问题,其中有一个问题是关于资源找不到的问题,百度了半天,也没有相关的文章 C:\Users.gradle\caches\transforms-1 ...

  5. Android Studio 升级到3.0后出现编译错误\.gradle\caches\transforms-1\files-1.1\*****-release.aar...

    Android Studio 升级到3.0后出现各种编译问题,其中有一个问题是关于资源找不到的问题,百度了半天,也没有相关的文章 C:\Users.gradle\caches\transforms-1 ...

  6. Android Studio 配置OpenCV4.4.0 不用安装OpenCV Manager (泪崩居然用了礼拜天2天的时间居然还没配置成功,今天又看了下配置成功了)

    Android Studio 配置OpenCV4.4.0 ,说来惭愧居然用户礼拜天2天的时间呢,期间遇到的问题大致有4个问题 这里我也总结出了最后在列举出来,(可能是新版和之前旧版本不一样的问题按照网 ...

  7. android studio 无法输入中文,Android Studio 升级到3.0后输入法中文状态下无法选词的终极解决方案...

    AndroidStudio终于出3.0正式版了,内置了kotlin(虽然我安了插件一直能用).一直忍着没敢下rc版的好奇猫,总算装了正式版.当然,伴随每次大版本更新,总有一些恼人的后遗症,其中以gra ...

  8. Android Studio升级到3.0,抛出Aapt2Exception异常

    Android Studio升级到3.0,抛出Aapt2Exception异常 参考文章: (1)Android Studio升级到3.0,抛出Aapt2Exception异常 (2)https:// ...

  9. Android Studio安装教程及安装中出现问题的解决办法

    Android Studio安装教程及安装中出现问题的解决办法 Android Studio安装教程: 安装网址: AndroidDevtools下载:https://developer.androi ...

最新文章

  1. php 构造 析构,php 构造方法和析构方法
  2. Nginx访问控制_IP访问控制(http_access_module)原理、局限性、解决方法讲解
  3. 微信小程序——获取所有资讯接口数据
  4. 五分钟教你在Go-Bigger中设计自己的游戏AI智能体
  5. python常用数据结构_Python常见数据结构整理
  6. android多个拖动控件,Android使用WindowManager制作一个可拖动的控件
  7. cisco 的端口聚合
  8. java接口文档编写_java api接口文档怎么编写?
  9. Matlab矢量图导出PDF格式方式及LaTex图片排版技巧
  10. SpringBoot:AOP切面execution表达式
  11. flash 火狐总是崩溃_火狐浏览器flash插件崩溃怎么办?解决firefox经常出现Adobe Flash 插件已崩溃方法...
  12. html 恶意广告,电脑自动弹出恶意广告怎么处理
  13. typeof和instanceof的区别
  14. 美国程序员把工作外包给中国程序员,啥也不干年入 20 万美元,这操作也是骚...
  15. win7 查看php版本信息,window_Windows系统版本怎么看?2种查看windows版本的方法介绍,前段时间微软正式开始向Win7和 - phpStudy...
  16. arm 处理器的堆栈操作
  17. SQL Server 常用日期函数的运用
  18. 基于Python实现相机标定正畸并生成鸟瞰图
  19. CMOS/CCD图像传感器工作原理
  20. Lua的require小结

热门文章

  1. RFC4627中文版-JSON格式定义
  2. android fun os,FuntouchOS10,横空出世,操作体验不输EMUI和MIUI
  3. Redis实战 - 15 Redis事务机制和乐观锁实现
  4. Cdiscount、Allegro如何利用测评补单自养号提升店铺权重和流量
  5. h5如何动态获取键盘高度_js判断手机端键盘弹出从而调整输入框高度
  6. Thumbnailator给图片加水印并保证图片的质量(大小)
  7. java 导出个性 excel 表格
  8. windows下追踪路由
  9. 【愚公系列】2023年06月 网络安全(交通银行杯)-数据包分析
  10. MyBatis Mapped Statements collection already contains value for xx.please check file [x]