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

目录
  • 请求存储权限
  • 版本适配
    • Android 7.0 前
    • Android 7.0 后
    • Android 10.0
      • 什么是作用域
  • 举个栗子

请求存储权限

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

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

在代码中请求用户权限:

    // 权限请求码
    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 版本、权限适配的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android进程运行中权限被收回导致关闭的问题解决

    流程如图: MainActivity 跳转至 MainActivity2 再跳转至 MainActivity3MainActivity3 跳转至 设置,收回权限 一路返回 运行log如下 // 一路跳转,打开设置,收回权限 com.miss.soullink E/MainActivity: == onCreate == 25149 com.miss.soullink E/MainActivity: == onCreate == savedInstanceState null com.miss.s

  • Android6.0获取动态权限代码示例

    Android系统对所有的危险权限进行了分组,称为 权限组 .属于同一组的危险权限将自动合并授予,用户授予应用某个权限组的权限,则应用将获得该权限组下的所有权限(前提是相关权限在 AndroidManifest.xml 中有声明). 危险权限 和 权限组 列表如下: 在 AndroidManifest.xml 声明过的危险权限对应的权限组可以在系统 "设置" -> "应用" -> "应用信息" -> "权限"

  • Android端权限隐私的合规化处理实战记录

    目录 是什么 为什么 具体实践 一.Android各版本对权限的适配处理 1.1 早期的注册权限 1.2 动态权限授予 1.3 READ_PHONE_STATE权限的变化 二.隐私信息合规化处理 2.1 隐私信息获取告知的直接化和透明化 2.2 隐私信息获取和传输的安全化 2.3 部分隐私Api调用的严格化 三.遇到的一些问题和坑 总结 是什么 对客户端而言,权限隐私可分为权限和隐私两个大的方面. 权限为用户通过app内弹窗设置或者手机设置内对应app的权限设置方式给予对应app相应的权限,如电

  • Android普通应用升级为系统应用并获取系统权限的操作

    有时候使用某些api需要使用系统权限,如调用PackageInstaller的相关接口,需要android.permission.INSTALL_PACKAGES权限,该权限系统只会授权给系统应用,此时可以考虑将我们的应用升级为系统应用,升级为系统应用有两种方法: 1.将apk放到/system/app目录下,重启手机即可,此方法比较粗暴,而且需要修改/system目录的读写权限,因此需要root,而且随着Android系统版本对权限管理越来越严,root和修改读写权限更繁琐和复杂 2.添加sh

  • android9.0 默认apk权限添加方法

    1.默认赋予全部权限: 安卓动态要求用户允许添加权限,直接将如下代码中的final boolean grantPermissions = (args.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;改为final boolean grantPermissions=true便可. 文件路径:framework\base\services\core\java\com\android\server\pm\Pac

  • Android 如何实现动态申请权限

    OverView 今天在复习的时候,突然复习到我们的相机操作,但是对于相机操作,对于我来说比较复杂的是对于权限的操作.所有我们需要对我们的相机操作进行一些笔记的整理,加深记忆. 开发环境 Android Studio 3.6 Android 11(R) 该笔记使用 java 权限申请的基础知识 学习自:GOOGLE ANDROID DEVELOPERS 对于我们所需要申请的权限我们需要进行如下的操作. 我们需要将所有需要申请的权限添加到App/src/main/AndroidManifest.x

  • Android6.0 动态权限机制深入讲解

    前言 Android6.0以后引入了动态权限机制,一些系统权限的分配需要在app运行中进行分配,而不只是在AndroidManifest中指定. 本篇将针对动态权限的底层分配过程进行分析(基于Android-6.0.1). 权限分配 我们先看一下请求分配权限的代码 //frameworks/support/v4/java/android/support/v4/app/ActivityCompat.java public static void requestPermissions(final @

  • Android registerForActivityResult动态申请权限案例详解

    前言 这几天在做一个小工具app,结果在fragment里面动态申请权限提示原有的申请方法已经弃用,还画了很明显的删除线...这叫一个强迫症的我怎么受得了.赶紧网上找资料也找不出什么结果,看了官方文档才发现了有registerForActivityResult这么一个神奇好用的函数,可以代替我们现有的startActivityForResult和权限申请函数. 那么下面就分两种情况来讲一下如何使用这个函数动态申请权限. 一.申请单个权限 首先,我们需要定义一个launcher: Activity

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

    目录 请求存储权限 版本适配 Android 7.0 前 Android 7.0 后 Android 10.0 什么是作用域 举个栗子 请求存储权限 首先需要在 AndroidManifest.xml 文件中声明权限: <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 在代码中请求用户权限: // 权限请求码 private static final int PERMISS

  • 详解Android版本适配:9.0 Pie

    一.前言 本文主要是从官方文档中筛选出一些常见的适配项,若有任何纰漏或需要补充的,欢迎大家在评论区指出. 二.版本适配 1. 限制 HTTP 网络请求 Android 9.0 中限制了 HTTP(明文传输)网络请求,若仍继续使用HTTP请求,则会在日志中提示以下异常(只是无法正常发出请求,不会导致应用崩溃): java.net.UnknownServiceException: CLEARTEXT communication to xxx not permitted by network secu

  • 详解Android权限管理之RxPermission解决Android 6.0 适配问题

    前言: 上篇重点学习了Android 6.0的运行时权限,今天还是围绕着Android 6.0权限适配来总结学习,这里主要介绍一下我们公司解决Android 6.0权限适配的方案:RxJava+RxPermission.这里不再介绍Android 6.0运行时权限了,直接看下如何使用RxPermission. RxPermission: 用于适配Android 6.0新的权限模型的开源框架. 下载地址:点此下载 如何使用? 1.)在app module的build.gradle中添加如下配置 使

  • Android 6.0权限请求相关及权限分组方法

    Android M(6.0)API 23后加入了权限请求设置,APP需要使用某些权限需要主动申请. 权限分为3类,一组是Normal权限,无需申请,另一组是Dangerous,需申请,然后是特殊权限,需申请. 先看下Normal权限列表: 再看下Dangerous权限列表: 危险权限实际上才是运行时权限主要处理的对象,这些权限可能引起隐私问题或者影响其他程序运行.Android中的危险权限可以归为以下几个分组: CALENDAR CAMERA CONTACTS LOCATION MICROPHO

  • android开发权限询问的示例代码

    现在基于信息安全问题,特别是版本是23以上权限越严格. 特别是拍照,读,写权限 一般权限允许过,下次就不用询问了的,所以很多应用都喜欢在首页或者启动页直接询问,不允许的就用不了1.下面给出封装好的类,至于什么时候调看项目需要 public class EasyPermissions { private static final String TAG = "EasyPermissions"; public interface PermissionCallbacks extends Act

  • Android 刘海屏适配总结(推荐)

    一.简介 随着 Apple 发布 iPhone X 之后,各大手机厂商也开始模仿这种刘海屏的设计,而且刘海屏手机的用户也是越来越大,前段时间将项目进行了所有主流厂商的刘海屏手机的适配,以便让刘海屏手机的用户也能有更好的体验. 二.刘海屏造成的 UI 显示问题 刘海屏手机因为比平常的手机多了一块顶部的遮挡性刘海,所以会造成顶部 Toolbar 以及搜索框的遮挡,而且有些厂商的手机(vivo.华为),默认是在「无状态栏」的界面将状态栏进行黑化显示,这时候会导致系统下移,从而导致底部的一些 UI 被截

  • Android如何快速适配暗黑模式详解

    直接上代码 public class DarkModeUtils { public static final String KEY_CURRENT_MODEL = "night_mode_state_sp"; private static int getNightModel(Context context) { SharedPreferences sp = context.getSharedPreferences(KEY_CURRENT_MODEL, Context.MODE_PRIV

  • Android uses-permission权限列表中文注释版

    android同时也限定了系统资源的使用,像网络设备,SD卡,录音设备等.如果你的应用希望去使用任何系统资源,我们必须去申请Android的权限.这就是<uses-permission>元素的作用. 一个权限通常有以下格式,用一个名字为name 的字符串去指导我们希望使用的权限. 复制代码 代码如下: <uses-permission android:name="string"/> 例如:想要获得networking APIs的使用权限,我们指定如下的元素作为

  • Android 手机屏幕适配解决办法

    0. 前言 Android的屏幕适配,即使得某一元素在Android不同尺寸.不同分辨率的手机上具备相同的显示效果,这个问题一直以来都是我们Android开发者不得不面对的问题.本文参考了很多前人的博客,并对这一问题做一个总结,力求精简明了. 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52690498 1. 基础概念 (1)屏幕尺寸,即手机对角线的物理尺寸 1英寸 = 2.54cm  常见手机尺寸有5英寸.5.5英寸.6英寸等

随机推荐