文章目录

  • 一、动态权限申请
  • 二、MediaStore 操作文件
  • 三、完整代码示例
    • 1、MainActivity 核心代码
    • 2、build.gradle 构建脚本
    • 3、清单文件
  • 五、相关文档资料


特别注意 Android 低版本中不能使用分区存储 API 操作文件 , 【错误记录】Android 低版本使用分区存储错误 ( IllegalArgumentException:no path was provided when inserting new file )





一、动态权限申请



进行 SD 卡读写操作前 , 必须先申请 SD 卡读写的动态权限 ;


动态权限参考 :

  • 【Android 应用开发】Google 官方 EasyPermissions 权限申请库 ( 最简单用法 | 一行代码搞定权限申请 | 推荐用法 )
  • 【Android 应用开发】Google 官方 EasyPermissions 权限申请库 ( 完整代码示例 | 申请权限 | 申请权限原理对话框 | 引导用户手动设置权限对话框 )

清单文件中的配置 :

<manifest     xmlns:android="http://schemas.android.com/apk/res/android"    package="kim.hsl.file">    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />    <application />manifest>

构建脚本中的配置 :

dependencies {         // 使用 Android X 的应用添加该依赖    implementation 'pub.devrel:easypermissions:3.0.0'}

Activity 中的权限申请源码 : 分支一是有权限的情况下的后续处理 , 分支二是申请动态权限 ;

    @AfterPermissionGranted( 100 )    fun doSomethingWithPermissions(){             if(EasyPermissions.hasPermissions(this,                        Manifest.permission.READ_EXTERNAL_STORAGE,                        Manifest.permission.WRITE_EXTERNAL_STORAGE)){                 // 分支一 : 如果有上述权限, 执行该操作            Toast.makeText(this, "权限申请通过", Toast.LENGTH_LONG).show()        }else{                 // 分之二 : 如果没有上述权限 , 那么申请权限            EasyPermissions.requestPermissions(                    this,                    "权限申请原理对话框 : 描述申请权限的原理",                    100,                    Manifest.permission.READ_EXTERNAL_STORAGE,                    Manifest.permission.WRITE_EXTERNAL_STORAGE            )        }    }




二、MediaStore 操作文件



在 Android 11 11 11 之后 , 不能使用 File 进行文件操作 , 需要使用 MediaStore 进行文件操作 ,

MediaStore 的如下内部类 Files , Images , Downloads , Audio , Video , 负责相应目录的文件操作 , 分别对应外置存储中的 Document , Pictures , Download , Music , Movies 目录 ;

如 : MediaStore 下的 Images 内部类 , 负责 Pictures 下的文件操作 ;

package android.provider;public final class MediaStore {         public static final class Images {             public Images() {                 throw new RuntimeException("Stub!");        }    }}

一个 文本文件 , 只能存储在 Download 和 Documents 目录下 , Download 目录可以存放任何类型的文件 , Documents 目录只能存储文本文件 ;

首先通过 MediaStore 获取 Files 内部类对象 , 调用该内部类的 getContentUri(“external”) , 即可获取在 【Android 文件管理】分区存储 ( 分区存储机制 和 文件索引数据 ) 四、文件索引数据库 博客章节提到的文件索引数据库 , 然后就可以通过 ContentValues 向其中插入数据 ;

获取数据库 :

        // 操作 external.db 数据库        // 获取 Uri 路径        var uri: Uri = MediaStore.Files.getContentUri("external")

插入数据时 , 构造 ContentValues 数据结构 , 主要是设置 external.db 数据库中 files 数据表对应的条目 , 设置该条目的主要字段值 ;

构造 ContentValues 数据 :

        // 将要新建的文件的文件索引插入到 external.db 数据库中        // 需要插入到 external.db 数据库 files 表中, 这里就需要设置一些描述信息        var contentValues: ContentValues = ContentValues()        // 设置插入 external.db 数据库中的 files 数据表的各个字段的值        // 设置存储路径 , files 数据表中的对应 relative_path 字段在 MediaStore 中以常量形式定义        contentValues.put(MediaStore.Downloads.RELATIVE_PATH, "${Environment.DIRECTORY_DOWNLOADS}/hello")        // 设置文件名称        contentValues.put(MediaStore.Downloads.DISPLAY_NAME, "hello.txt")        // 设置文件标题, 一般是删除后缀, 可以不设置        contentValues.put(MediaStore.Downloads.TITLE, "hello")

ContentValues 构造成功后 , 使用 ContentResolver 将数据插入数据库中 ; 系统会自动创建对应的文件 ;

向数据库中插入数据 :

        // uri 表示操作哪个数据库 , contentValues 表示要插入的数据内容        var insert: Uri = contentResolver.insert(uri, contentValues)!!

系统自动创建的文件是一个目录文件 , 向其中写出 “Hello World” 文本数据 , 即可完成相关文件创建 ;

通过返回的 Uri 打开输出流 , 向文件中写出数据 :

        // 向 Download/hello/hello.txt 文件中插入数据        var os: OutputStream = contentResolver.openOutputStream(insert)!!        var bos = BufferedOutputStream(os)        bos.write("Hello World".toByteArray())        bos.close()

启动 Android 11 系统的模拟器 , 然后部署该应用 , 文件创建成功 ;





三、完整代码示例





1、MainActivity 核心代码


package kim.hsl.fileimport android.Manifestimport android.content.ContentValuesimport android.net.Uriimport android.os.Bundleimport android.os.Environmentimport android.provider.MediaStoreimport android.widget.Toastimport androidx.appcompat.app.AppCompatActivityimport pub.devrel.easypermissions.AfterPermissionGrantedimport pub.devrel.easypermissions.EasyPermissionsimport java.io.BufferedOutputStreamimport java.io.OutputStreamclass MainActivity : AppCompatActivity() {         override fun onCreate(savedInstanceState: Bundle?) {             super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        // 动态权限获取        doSomethingWithPermissions()    }    @AfterPermissionGranted( 100 )    fun doSomethingWithPermissions(){             if(EasyPermissions.hasPermissions(this,                        Manifest.permission.READ_EXTERNAL_STORAGE,                        Manifest.permission.WRITE_EXTERNAL_STORAGE)){                 // 分支一 : 如果有上述权限, 执行该操作            Toast.makeText(this, "权限申请通过", Toast.LENGTH_LONG).show()            // Android 11 中创建文件            createFile()        }else{                 // 分支二 : 如果没有上述权限 , 那么申请权限            EasyPermissions.requestPermissions(                    this,                    "权限申请原理对话框 : 描述申请权限的原理",                    100,                    Manifest.permission.READ_EXTERNAL_STORAGE,                    Manifest.permission.WRITE_EXTERNAL_STORAGE            )        }    }    /**     * 创建文件     * 在 Download 目录下创建 hello.txt     */    fun createFile(){             // 操作 external.db 数据库        // 获取 Uri 路径        var uri: Uri = MediaStore.Files.getContentUri("external")        // 将要新建的文件的文件索引插入到 external.db 数据库中        // 需要插入到 external.db 数据库 files 表中, 这里就需要设置一些描述信息        var contentValues: ContentValues = ContentValues()        // 设置插入 external.db 数据库中的 files 数据表的各个字段的值        // 设置存储路径 , files 数据表中的对应 relative_path 字段在 MediaStore 中以常量形式定义        contentValues.put(MediaStore.Downloads.RELATIVE_PATH, "${Environment.DIRECTORY_DOWNLOADS}/hello")        // 设置文件名称        contentValues.put(MediaStore.Downloads.DISPLAY_NAME, "hello.txt")        // 设置文件标题, 一般是删除后缀, 可以不设置        contentValues.put(MediaStore.Downloads.TITLE, "hello")        // uri 表示操作哪个数据库 , contentValues 表示要插入的数据内容        var insert: Uri = contentResolver.insert(uri, contentValues)!!        // 向 Download/hello/hello.txt 文件中插入数据        var os: OutputStream = contentResolver.openOutputStream(insert)!!        var bos = BufferedOutputStream(os)        bos.write("Hello World".toByteArray())        bos.close()    }}


2、build.gradle 构建脚本


引入 pub.devrel:easypermissions:3.0.0 依赖库 ; ( 其它省略 )

dependencies {         // 使用 Android X 的应用添加该依赖    implementation 'pub.devrel:easypermissions:3.0.0'}


3、清单文件


配置 SD 卡读写权限 ; ( 其它省略 )

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="kim.hsl.file">    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />manifest>




五、相关文档资料



Android 文件处理参考文档 :

  • 数据和文件存储概览 : https://developer.android.google.cn/training/data-storage

  • 访问应用专属文件 : https://developer.android.google.cn/training/data-storage/app-specific#kotlin

  • 保存到共享的存储空间 : https://developer.android.google.cn/training/data-storage/shared

  • 管理存储设备上的所有文件 : https://developer.android.google.cn/training/data-storage/manage-all-files

  • 分享文件 : https://developer.android.google.cn/training/secure-file-sharing

  • 应用安装位置 : https://developer.android.google.cn/guide/topics/data/install-location

  • Android 存储用例和最佳做法 : https://developer.android.google.cn/training/data-storage/use-cases

  • FileProvider : https://developer.android.google.cn/reference/androidx/core/content/FileProvider


博客源码 :

  • GitHub : https://github.com/han1202012/File

  • CSDN : https://download.csdn.net/download/han1202012/18832417

更多相关文章

  1. 让Ubuntu和Android同时运行(Ubuntu on Android)
  2. manifest文件
  3. Android上层启动过程的几个关键点
  4. Android(安卓)SDK相关介绍
  5. android九宫格实现
  6. Android解析ClassLoader(二)Android中的ClassLoader
  7. Android(安卓)Opencore
  8. Android对话框里面的输入值获取不到,空指针异常
  9. Android高手进阶教程(四)之----Android(安卓)中自定义属性(attr.

随机推荐

  1. 【Flutter开发】------修改图标、应用名
  2. android笔试题
  3. Android核心模块内容概述
  4. Android(安卓)ContentProvider的使用和理
  5. Android横竖屏切换小结
  6. android布局layout中的一些属性
  7. Android开机log和常见异常的分析
  8. Android(安卓)Sensor详解(5)搭建adsp firmw
  9. ListView使用技巧(二):相关设置
  10. android:configChanges 横竖屏切换的生命