Android10权限变更—分区存储

  1. androidQ的变更

    • 独立的存储沙盒:应用通过文件路径保存的文件都会被保存在应用的沙盒中,App卸载沙盒内的所有内容会被删除
    • 共享目录:有些文件譬如图片等,开发者不希望这些文件随着app的卸载被删除,可以将他们通过谷歌提供的MediaProvider或者SAF保存到共享目录中。
    • 权限:app读取自己沙盒和自己存放在公共目录下的文件是不需要申请任何权限的,但是如果app要读取其他应用创建的公共目录下的文件,是需要申请READ_EXTERNAL_STORAGE权限;系统只提供了读的权限,所以是无法通过申请写的权限修改其他应用创建的文件。
    • 路径问题:读取私有目录下的文件,可以直接使用文件的路径直接获取。应用和其他应用保存在共有目录的下的多媒体文件,都只能通过Media一些方法进行获取。通过路径方式是获取不到的。
  2. androidQ的存储结构示意图

  3. 向沙盒内写入文件

    建议将: 内部存储:/data/user/0/应用包名/files和内置外部存储:/storage/emulated/0/Android/data/应用包名/files作为沙盒。

    代码:

    /** 将图片存到沙盒中,建议写到this.getExternalFilesDir(null)文件夹下* this.getFilesDir()所在的内存储空间有限,满了影响手机运行* */
    private void saveImageToBox(String fileName, Bitmap bitmap) {final String ImagePath = "zwk";try {File PictureFile = this.getFilesDir(); // 内存储的私有目录中//File PictureFile = this.getExternalFilesDir(null); // 内置外存储的私有文件夹File ImageFileDirectory = new File(PictureFile + File.separator + ImagePath);if (ImageFileDirectory.exists()) {File photoFile = new File(ImageFileDirectory + File.separator + fileName);FileOutputStream fileOutputStream = new FileOutputStream(photoFile);bitmap.compress(Bitmap.CompressFormat.PNG, 90, fileOutputStream);fileOutputStream.flush();fileOutputStream.close();} else if (ImageFileDirectory.mkdir()) {File photoFile = new File(ImageFileDirectory + File.separator + fileName);FileOutputStream fileOutputStream = new FileOutputStream(photoFile);bitmap.compress(Bitmap.CompressFormat.PNG, 90, fileOutputStream);fileOutputStream.flush();fileOutputStream.close();}} catch (Exception e) {Log.d("zwk", "异常了");}
    }
    
  4. 读写公共目录

    • 写入公共目录

      • 通过提供的ContentValues设置一些文件的信息;
      • getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)方法设置文件插入文件的Uri;
      • getContentResolver().openOutputStream(uri)和读取自己公共目录下创建的文件bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); 写入文件

    代码:

    /**  将文件写入到公共目录中去,需要根据文件的类型,找到合适的Environment.DIRECTORY_xx路径*  如:图片可以放到Environment.DIRECTORY_PICTURES|Environment.DIRECTORY_DCIM中*  文件不同,使用的MediaStore.xx不同 *  如:图片需要使用MediaStore.Images.Media* * */
    private void saveImageToPublic(String fileName, Bitmap bitmap) {ContentValues contentValues = new ContentValues();contentValues.put(MediaStore.Images.Media.DESCRIPTION, "公共目录下的图片的描述"); // 文件的描述contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, fileName); // 设置保存到公共目录下的文件的名称contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/*"); //文件的类型contentValues.put(MediaStore.Images.Media.IS_PENDING, 0); // 是否被其他应用可以读,0可读,1不读contentValues.put(MediaStore.Images.Media.TITLE, "public图片的标题");//文件的标题if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // 适配,大于10版本contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/Camera"); //可以是Pitutes/xxx} else {  // 小于10需要使用DATAcontentValues.put(MediaStore.Images.Media.DATA,Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath());}Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); if (uri != null) {try {OutputStream outputStream = getContentResolver().openOutputStream(uri);if (outputStream != null) {bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); // 通过流写入outputStream.flush();outputStream.close();}} catch (FileNotFoundException e) {e.printStackTrace();Log.d("zwk", "文件么有找到");} catch (IOException e) {e.printStackTrace();Log.d("zwk", "流关闭异常");}}
    }
    

    MediaStore.Images.Media.IS_PENDING 设置0,应用自己和其他应用都可以访问,设置1,应用自己和其他应用都无法访问(我试的都不可以访问,这个暂时算是没搞清楚)。

    错误方式:通过文件的路径将图片保存到公共目录,会报异常:java.io.FileNotFoundException(Permission denied)。
    错误代码:

     /** 在Q中通过文件的地址去创建文件保存图片是会报错。* 所以只能使用Android 提供的Api的方式存储 * */private void saveToPublic(String fileName, Bitmap bitmap)  {final String ImagePath = "outZwk";try {File PictureFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); // 内存储的私有目录中File ImageFileDirectory = new File(PictureFile + File.separator + ImagePath);if (ImageFileDirectory.exists()) {File photoFile = new File(ImageFileDirectory + File.separator + fileName);FileOutputStream fileOutputStream = new FileOutputStream(photoFile);bitmap.compress(Bitmap.CompressFormat.PNG, 90, fileOutputStream);fileOutputStream.flush();fileOutputStream.close();} else {boolean isCreate=ImageFileDirectory.mkdir();File photoFile = new File(ImageFileDirectory + File.separator + fileName);FileOutputStream fileOutputStream = new FileOutputStream(photoFile);bitmap.compress(Bitmap.CompressFormat.PNG, 90, fileOutputStream);fileOutputStream.flush();fileOutputStream.close();}} catch (Exception e) {e.printStackTrace();Log.d("zwk", "异常了"+e.getMessage());}}
    

    读取其他应用创建的公共目录下的文件

     -  首先应该申请READ_EXTERNAL_STORAGE权限,方法和6.0一样- 根据条件查存储来图片的Uri-  根据Uri获取到图片
    

    代码:

    /** 根据Uri获取Bitmap* 其中Uri格式:content://media/external/images/media/26  26代表图片的_ID** */
    private Bitmap getPublicImage() {Uri uri = qureyPublicImageUri();if (uri == null) {Log.d("zwk", "获取Uri失败");return null;}ContentResolver contentResolver = getContentResolver();try {ParcelFileDescriptor parcelFileDescriptor = contentResolver.openFileDescriptor(uri, "r");FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);parcelFileDescriptor.close();return bitmap;} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;
    }/**查询图片的Uri,只能通过这种谷歌提供的接口的方法query()获取文件* query 第一个参数:所有图片所在的Uri地址*       第二个参数:String[] 里面存放的是:需要查询的信息。如:没有_ID这项,直接在curror中获取_ID会报空  指针*       第三个参数:查询的条件:*       第四个参数:String[] :查询条件的限制** */
    private Uri qureyPublicImageUri() {List<Uri> listUri = new ArrayList<>();Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,new String[]{MediaStore.Images.Media._ID}, MediaStore.Images.Media.DISPLAY_NAME + "=?", new String[]{"publicDir图片01.jpg"}, null); //MediaStore.Images.Media.DISPLAY_NAME + "=?", new String[]{"publicDir图片01.jpg"},while (cursor.moveToNext()) {int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));Uri imgUri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + File.separator + id);listUri.add(imgUri);}return listUri.size() == 0 ? null : listUri.get(0); // 返回列表中第一个Uri
    }
    
  5. 删除操作

  • 应用沙盒文件的删除
    可以直接通过路径进行删除

  • 应用删除共有目录下创建的文件

        /** 删除自己共有目录下的文件* 1.先查询到文件的Uri* 2.待用delete方法删除* */
    private void deleteSelfPublicFile() {Uri imgUri = null;Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,new String[]{MediaStore.Images.Media._ID}, MediaStore.Images.Media.DISPLAY_NAME + "=?", new String[]{"publicDir图片01.jpg"}, null);while (cursor.moveToNext()) {int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));imgUri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + File.separator + id);}if (imgUri!=null){int rowNum=getContentResolver().delete(imgUri,null,null); // 返回删除的行号if (rowNum!=-1){Log.d("zwk","删除成功");}}
    }
    
  • 应用删除其他应用创建的文件

    除非是系统级别的应用可以具有删除和修改其他应用的图片和多媒体文件,否则使用getContentResolver().delete()方法删除其他应用的工作目录文件,会报以下的异常:
    android.app.RecoverableSecurityException: com.example.thirthqdemo has no access to content://media/external/images/media/27

6.SAF
andorid在4.4的时候,就引入的存储访问框架–ASF(https://developer.android.google.cn/guide/topics/providers/document-provider)
其实就是用Intent打开选择器进行访问文件.

Android Q中,我们使用MediaStore只能访问公共目录下多媒体文件,其他类型的文件,如将文件存储到downloads下我们只能使用ASF这种方式.(用户需要自己选择下文件)

  • Intent.ACTION_OPEN_DOCUMENT 打开文件选择器
  • Intent.ACTION_CREATE_DOCUMENT 创建一个文件
  • intent.addCategory(Intent.CATEGORY_OPENABLE) 过滤器只显示可以打开的
  • intent.setType() 文件类型MIME过滤器

代码:

  • 创建文件,

     Intent intentCreate = new Intent(Intent.ACTION_CREATE_DOCUMENT);intentCreate.addCategory(Intent.CATEGORY_OPENABLE);intentCreate.setType("text/plain"); // 创建文件夹可以使用:DocumentsContract.Document.MIME_TYPE_DIRintentCreate.putExtra(Intent.EXTRA_TITLE, "创建的文档.txt");startActivityForResult(intentCreate, 0X002);@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {if (resultCode == RESULT_OK) {switch (requestCode) {case 0x002:Uri txtUri = data.getData();Log.d("zwk", "地址是:" + txtUri.toString());try {ParcelFileDescriptor txtEdit = getContentResolver().openFileDescriptor(txtUri, "w");FileOutputStream fileOutputStream = new FileOutputStream(txtEdit.getFileDescriptor());fileOutputStream.write("测试的内容".getBytes());fileOutputStream.close();txtEdit.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}break;}}
  • 打开某种文件

 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);  // 打开文件选择器intent.addCategory(Intent.CATEGORY_OPENABLE); // 添加多滤器-只可以打开的文件intent.setType("image/*"); // 类型过滤器-仅显示类型是图片类型startActivityForResult(intent, 0x001);@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);if (resultCode == RESULT_OK) {switch (requestCode) {case 0x001:if (data != null) {Uri mUri = data.getData(); // 获取选中图片的uriLog.d("zwk", "图片的地址是:" + mUri.toString());Cursor cursor = getContentResolver().query(mUri, null, null, null, null); // 根据Uri获取图片的信息try {if (cursor != null && cursor.moveToFirst()) {String imageName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));Log.d("zwk", "图片的名称:" + imageName);int size = cursor.getColumnIndex(OpenableColumns.SIZE);Log.d("zwk", "图片的大小:" + size);}} finally {cursor.close();}// 获取图片Bitmap,在线程中获取图片,然后显示到UI中new AsyncTask<Uri, Void, Bitmap>() {@Overrideprotected Bitmap doInBackground(Uri... uris) {Uri uri = uris[0];Bitmap imageBitmap = null;try {ParcelFileDescriptor mParcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r");FileDescriptor fileDescriptor = mParcelFileDescriptor.getFileDescriptor();imageBitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);mParcelFileDescriptor.close();} catch (FileNotFoundException e) {e.printStackTrace();Log.d("zwk", "异常");} catch (IOException e) {e.printStackTrace();Log.d("zwk", "异常2");}return imageBitmap;}@Overrideprotected void onPreExecute() {super.onPreExecute();}@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);if (bitmap == null) {Log.d("zwk", "bitmap为空");}iamgeView.setImageBitmap(bitmap);}@Overrideprotected void onProgressUpdate(Void... values) {super.onProgressUpdate(values);}@Overrideprotected void onCancelled(Bitmap bitmap) {super.onCancelled(bitmap);}@Overrideprotected void onCancelled() {super.onCancelled();}}.execute(mUri);}break;
}
  • 删除某个文件
   Intent intent4 = new Intent(Intent.ACTION_OPEN_DOCUMENT);  // 打开文件选择器intent4.addCategory(Intent.CATEGORY_OPENABLE); // 添加多滤器-只可以打开的文件intent4.setType("text/plain"); // 类型过滤器-仅显示类型是图片类型startActivityForResult(intent4, 0x004);case 0X004: //查询后删除文件try {DocumentsContract.deleteDocument(getContentResolver(), data.getData());} catch (FileNotFoundException e) {e.printStackTrace();}

AndroidQ适配之存储权限的变更相关推荐

  1. 安卓11上的存储权限问题

    这篇文章,想来发布的有些晚了,安卓11已经发布多时了,关于安卓11上的存储权限变更的文章数不胜数,所以这篇文章只做为自己的一个简单的记录吧! 在说11之前,我们先回忆以下10上存储权限的变更:每个应用 ...

  2. android读写相册权限,androidQ 关于存储权限相册图片

    关于存储位置 内部存储(Internal Storage) //路径:(data/data/packageName/cache) getCacheDir() //路径:(data/data/packa ...

  3. requestPermissions读写手机存储权限_Android 11 开发者常见问题: 存储 | FAQ?第二期

    我们在 Android 10 中首次引入了 "分区存储" 的概念,旨在保护应用和用户数据并减少文件混乱.自此之后我们收到了开发者们的宝贵建议,这些建议有助于我们对该功能的持续优化, ...

  4. requestPermissions读写手机存储权限_Android 11 开发者常见问题:存储|FAQ?第二期

    我们在 Android 10 中首次引入了 "分区存储" 的概念,旨在保护应用和用户数据并减少文件混乱.自此之后我们收到了开发者们的宝贵建议,这些建议有助于我们对该功能的持续优化, ...

  5. Android KitKat 外部存储权限分析

    不知道你有么有发现,来自菜鸟的成长史:http://blog.csdn.net/zjbpku/article/details/25161131, KitKat之后的版本不再支持用户对外置SDcard( ...

  6. Android SDK22以下 读写手机存储权限获取失败问题处理方法

    针对小米手机sdk22以下sdk获取不到文件 存储权限问题. /****************** Android SDK22 读写手机存储权限获取问题处理方法 ****************** ...

  7. tar解压造成目录权限发生变更解决

    我用的root用户解压的包,包的拥有者和所属组都是root,但是我tar zxvf xxxxx.tar.gz -C /home之后,出来的包的拥有者和所属组怎么成了拥有者10132,所属组user 因 ...

  8. Android存储权限

    Android存储权限(备忘) 访问外置存储并不是一定需要以下这些权限(Android 4.4以上) WRITE_EXTERNAL_STORAGE READ_EXTERNAL_STORAGE 通过 g ...

  9. 安卓6.0及以上怎样给第三方apk默认开启存储权限

    第三方apk需要开启存储权限的,apk自身并没有请求开启存储权限的,只需要在Activity中校验权限即可. 首先在manifest中加上SD卡权限 <uses-permission andro ...

  10. android 10 apk 存储适配,AndroidQ(10) 分区存储适配方法

    创建文件夹 下载文件 InputStream is = null; byte[] buf = new byte[2048]; int len = 0; FileOutputStream fos = n ...

最新文章

  1. JavaScript常规语法小总节
  2. Java+Selenium爬贴吧
  3. linux alsa 录音程序,Linux下alsa直接录音代码
  4. Unity2D实现贴图凹凸感并接受实时光照效果
  5. python分子化学模拟_python简单实现gillespie模拟
  6. java网络编程与分布式计算_Java网络编程与分布式计算
  7. Linux tty驱动程序一 架构
  8. My安卓知识5--百度地图api的使用,周边信息检索
  9. 使用setsockopt TCP_NODELAY禁用 Nagle算法
  10. mysql怎么进行删除操作_利用PHP怎么对MySQL数据库进行删除操作
  11. 地理高程数据SRTM3简介
  12. 华为USG6000系列防火墙的Console密码重置过程
  13. adobe软件卸载不了怎么办?那就使用dobe官方清理工具吧!
  14. C#第九天面向对象的学习
  15. 图片太大上传不了怎么缩小?jpg图片压缩大小的方法
  16. 并发编程之 ThreadLocal 源码剖析
  17. Cocos Creator游戏开发教程 学习笔记
  18. 【win10】windows音频设备图形隔离占CPU高解决办法
  19. Invalid packaging for parent POM , must be “pom“ but is “jar“
  20. 微积分--极值点不一定是升降分界点、升降分界点一定是极值点

热门文章

  1. 英文网页翻译中文失败、QQ文件打不开、QQ系统消息打不开等
  2. 电脑怎么设置计算机系统,细说电脑怎么设置wifi
  3. Android客户端如何使用cookie
  4. 图片的单点触摸移动与多点触摸移动缩放
  5. 20个最棒的英文电子书免费下载网站
  6. beta函数与置信度估计
  7. 英文科技论文写作与学术报告Lecture1习题答案
  8. 利用google搜索自己的博客
  9. 报表分析工具有哪些?常见开源报表工具和商用报表工具介绍
  10. 高拍仪Twain接口功能