热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android版本、权限适配相关总结

针对Android6.0(API23)已以上版本,Google增强全新的权限,应用程序在使用敏感权限(如拍照、查阅联系人或存储)时需要先征求用户必须赢得用户同意。

请求存储权限

首先需要在 AndroidManifest.xml 文件中声明权限:


在代码中请求用户权限:

    // 权限请求码
    private static final int PERMISSION_REQ_ID = 0;  
    // 请求权限
    private static final String[] REQUESTED_PERMISSIOnS= {
            Manifest.permission.READ_EXTERNAL_STORAGE
    };

    ...

     // 判断有没有存储权限
    if (checkSelfPermission(REQUESTED_PERMISSIONS[0],PERMISSION_REQ_ID)){
        //YSE 
    }else {
        //NO
    }
    private boolean checkSelfPermission(String permissions,int requestCode){
        if (ContextCompat.checkSelfPermission(this,permissions) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode);
            return false;
        }
        return true;
    }

    // 重写此方法,接收用户授权回调
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        Log.i(TAG, "onRequestPermissionsResult: requestCode =" + requestCode
                +"\n,permissiOns=" + Arrays.toString(permissions)
                +"\n,grantResults =" + Arrays.toString(grantResults));
        if (requestCode == PERMISSION_REQ_ID){
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
                //用户同意权限
            }else {
                //用户拒绝权限
            }
        }
    

版本适配

从 Android 6.0 到 Android 10 存储/访问文件功能,有发生了很多变化。

Android 7.0 前

在Android 7.0 以前我们访问内存中的文件可以通过 Uri.fromFile,将 File 转换成 Uri 对象,这个 Uri 对象表示这本地真实路径。 访问一个图片:

String fileName = "default_Image.jpg";
File file = new File("file_path", fileName);
Uri uri = Uri.fromFile(file);

Android 7.0 后

在 7.0 后,这种通过真实路径来获取的 Uri 被认为是不安全的,所以提供了一种新的解决方案,就是通过 FileProvide 来实现文件的访问,FileProvider 是一种比较特殊的内容提供器,他使用了类似于内容提供器的机制来对数据进行保护。 访问一个图片:

    File file = new File(CACHE_IMG, "file_name");
    Uri imageUri = FileProvider.getUriForFile(activity,"com.sandan.fileprovider", file); //这里进行替换uri的获得方式

然而上面这种真的好吗,对用开发者而且这算是好处吧,但是对用用户而言,上述的无疑一些流氓作用,因为开发者完全可以访问的内存中的所有位置,并作出一些改变,导致 SD 卡中的空间变得非常乱,即使卸载了 app,但是一些垃圾文件却还在内存中。

Android 10.0

在 Android 10.0 ,为了解决上述问题,Google 在 Android 10.0 中加入了 作用域功能。

什么是作用域

就是 Android 系统对 SD 卡做了很大的限制,从 Android 10.0 开始,每个程序只能有权在自己的外置存储空间关联的目录下读取和创建相应的文件,也称作沙箱。获取该目录的代码是:getExternalFilesDir() ,关联的目录路径大致如下:

 Html CSS Javascript Vb vbs Asp PHP Perl Python Ruby C# C++ SQL Delphi Diff Groovy Java JavaFX ActionScript3 Bash/shell powershell Plain Text Scala XML显示语言名称 显示行号 允许折叠 

将数据放在这个目录下,你可以使用之前的方法对文件进行读写,不需要作出任何变更和适配。但是这个文件夹中的文件会随着应用卸载而被随之删除。 那如果需要访问其他目录怎么办呢?比如获取相册中的图片,向相册中添加一张图片。为此,Android 系统针对系统文件类型进行了分类:图片,音频,视频 这三类文件可以通过 MediaStore API 来进行访问,这种称为共享空间,其他的系统文件需要使用 系统的文件选择器来进行访问,另外,如果程序向媒体库写入图片,视频,音频,将会自动用于读写权限,不需要额外申请权限,如果你要读取其他程序向媒体贡献的图片,视频,音频,则必须要申请 READ_EXTERNAL_STORAGE 权限,WRITE_EXTERNAL_STORAGE 权限会在未来的版本中被废弃。

举个栗子

举例说明:有一张本地图片,向这张图片添加水印,并保存到相册。

直接上代码:

    /**
     * 保存图片到相册
     *
     * @param context 上下文
     * @param text 水印文字
     */
    private void savePhotoAlbum(final Context context, final String text) {
        //这里开启子线程,防止堵塞。
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //从本地获取一张图片,转成Bitmap
                    Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_info);
                    //在沙箱中创建文件,名称:info.jpg
                    File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "info.jpg");
                    //判断文件是否存在,不存在创建文件。
                    if (!file.exists()) {
                        file.createNewFile();
                    }
                    // 向图片添加水印
                    Bitmap newBitmap = addInfoWatermark(context, bitmap, text);
                    // 更新相册
                    updatePhotoAlbum(context, newBitmap, file);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start(); //开始线程
    }

   /**
     * 保存到相册
     *
     * @param context 上下文
     * @param src  源图片
     * @param text 水印文字
     */
    private Bitmap addInfoWatermark(final Context context, Bitmap src, String text) {
        //判断图片/水印文字 是否为空
        if (isEmptyBitmap(src) || text == null ) {
            return null;
        }
        // 从源图片复制一份
        Bitmap ret = src.copy(src.getConfig(), true);
        // 初始化画笔
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
         // 初始化画布
        Canvas canvas = new Canvas(ret);
        // 水印文字:黑色
        paint.setColor(Color.BLACK);
        // 文字大小:19dp
        paint.setTextSize(dip2px(context, 19));
         // 开始绘画
        canvas.drawText(text, 10, 10 , paint);
        // 循环利用资源
        if (!src.isRecycled()) {
            src.recycle(); 
        }
        return ret;
    }

    /**
     * 保存到相册
     *
     * @param context 上下文
     * @param src  源图片
     * @param file 要保存到的文件
     */
    private void savePhotoAlbum(final Context context, Bitmap src, final File file) {
        //判断图片 是否为空
        if (isEmptyBitmap(src)) {
            return;
        }
        // 保存文件
        OutputStream outputStream;
        try {
            //输出这个文件
            outputStream = new BufferedOutputStream(new FileOutputStream(file));
            // 压缩
            src.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
            // 循环利用资源
            if (!src.isRecycled()) {
                src.recycle();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        // 更新图库,这个在 Android 6.0 和 Android 10.0 更新图库,存在差异。
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {  
            // Android 10.0 及以上
            // 创建 ContentValues 对象,准备插入数据
            ContentValues values = new ContentValues();
            values.put(MediaStore.MediaColumns.DISPLAY_NAME, file.getName());
            values.put(MediaStore.MediaColumns.MIME_TYPE, getMimeType(file));
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
            ContentResolver cOntentResolver= context.getContentResolver();
            // 插入数据,返回所插入数据对应的Uri
            Uri uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
            if (uri == null) {
                return;
            }
            try {
                // 获取刚插入的数据的Uri对应的输出流
                outputStream = contentResolver.openOutputStream(uri);
                FileInputStream fileInputStream = new FileInputStream(file);
                // 从一个流复制到另一个流上
                FileUtils.copy(fileInputStream, outputStream);
                //关闭流
                fileInputStream.close();
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            // android 6.0 - 10.0
            // 扫描文件
            MediaScannerConnection.scanFile( context.getApplicationContext(), new String[]{file.getAbsolutePath()}, new String[]{"image/jpeg"},
                    new MediaScannerConnection.OnScanCompletedListener() {
                        @Override
                        public void onScanCompleted(String path, Uri uri) {
                            //通知相册更新
                            // 插入图片
                            MediaStore.Images.Media.insertImage( context.getContentResolver(),  BitmapFactory.decodeFile(path), file.getName(), null);
                            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                            Uri u = Uri.fromFile(file);
                            intent.setData(u);
                            context.sendBroadcast(intent);  // 发广播通知,更新相册
                        }
                    });
        }
    }

    /**
     * Bitmap对象是否为空。
     */
    private static boolean isEmptyBitmap(Bitmap src) {
        return src == null || src.getWidth() == 0 || src.getHeight() == 0;
    }

    /**
     * 获取 Mime 类型
     *
     * @param file 文件
     * @return Mime 类型
     */
    private static String getMimeType(File file) {
        FileNameMap fileNameMap = URLConnection.getFileNameMap();
        String type = fileNameMap.getContentTypeFor(file.getName());
        return type;
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    public int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

以上就是Android 版本、权限适配相关总结的详细内容,更多关于Android 版本、权限适配的资料请关注其它相关文章!


推荐阅读
  • 2021 年 10 月 TIOBE 指数榜:Python 超越 C 语言成 20 多年来的新霸主
    TIOBE更新了2021年10月份编程语言指数排行榜。榜单中共对100种编程语言进行了评级排名,其中Python成为“20多年来的新语言霸主”,以11. ... [详细]
  • 1、DashAPI文档Dash是一个API文档浏览器,使用户可以使用离线功能即时搜索无数API。程序员使用Dash可访问iOS,MacOS, ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 前端图片合成技术_靠谱的前端需要做哪些准备?
    Web前端开发源于传统的互联网,互联网普及让人才需求量居高不下,随着移动互联网的高速发展,移动终端的前端开发也越来越受到重视, ... [详细]
  • CentOs 7.3中搭建RabbitMQ 3.6单机多实例服务的步骤与使用
    CentOs7.3中搭建RabbitMQ3.6单机多实例服务的步骤与使用-RabbitMQ简介RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
author-avatar
程璇
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有