Android无需读写权限通过临时授权读写用户文件详解

目录
  • 正文
    • 这里插三段小说明,如果只想知道方法的可以直接跳过
  • 模拟获取用户的图片的逻辑
    • 模拟将文件写入用户目录的操作
    • 模拟获取用户文件夹控制权的操作
    • 通过Uri获取文件信息

正文

在进行需求开发的时候,我们总是避不开和用户的数据打交道,那提到获取用户的数据一定会想到的东西就是申请权限

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

在我刚学习安卓的时候,我是以为APP一定要声明了读写用户空间权限并且在用户授权之后才能获取到用户的文件,即使是做个简简单单的更换头像的功能,或者是升级APP时下载新的APK。对于后者,我们其实可以将升级的APK包放到我们应用的私有目录下(无需权限),对于前者,有什么比较轻量,适合快速开发需求的方法来满足呢。

这里插三段小说明,如果只想知道方法的可以直接跳过

  • 首先我们要明白,为什么谷歌要用读写权限来限制APP对用户文件的操作权。答案其实很明显,因为需要防止APP恶意侵犯用户隐私,或者是在用户的目录里存放大量的垃圾文件,在用户目录里存放的文件是不会随着APP的卸载而被删除的,所以如果所有APP都在用户的目录里存放文件(像是相册文件夹/下载文件夹),那用户的体验别提有多糟糕了。
  • 其次就是声明权限其实是有挺多弊端的,如果不是非必须的权限,其实谷歌是希望我们能不要就不要的。做过谷歌应用市场开发的就知道,你声明的每个权限都会在谷歌应用的详情页标注,这不仅仅是让用户一进来就觉得:"这个APP又要窥探我隐私",而且是让你在填应用的数据安全表单时更加地麻烦,因为你声明了读写权限,那你就要说明你的APP会获取用户的什么数据,如何保存,用户是否可以删除以及是否知情等等。还有就是你声明的权限越多,你的应用审核时间就会越长,这个我相信没有人觉得无所谓吧
  • 第三就是,Android11及以上的版本其实已经大削了WRITE_EXTERNAL_STORAGE这个权限,谷歌不再允许APP悄悄地在用户的外置存储目录里偷偷拉屎了,你在用户目录里创建什么目录存取什么数据都要在用户知情并且同意的情况下才能进行,而本文要介绍的方式是能兼容到Android13的,所以赶紧学起来吧^-^

模拟获取用户的图片的逻辑

我们需要拿到代表用户临时授权给APP的Uri

通过

val intent = Intent(Intent.ACTION_GET_CONTENT)
    .addCategory(Intent.CATEGORY_OPENABLE)
    //这里传的参数是你要获取的文件类型的mimeType
    .setType(mimeType)
startActivityForResult(intent,1024)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == 1024 && resultCode == RESULT_OK) {
        val uri = data?.data
        //这里获取到的uri就是用户临时授权的文件/文件夹的的标识
    }
}

或者

val launch = registerForActivityResult(ActivityResultContracts.GetContent()){uri->
//这里获取到的uri就是用户临时授权的文件/文件夹的的标识
}
//这里传的参数是你要获取的文件类型的mimeType
launch.launch("*/*")

启动系统的内容选择器让用户选择要分享给我们APP的文件,以获得文件的Uri

通过contentResolver打开文件的文件描述符FileDescriptor

val pfd : ParcelFileDescriptor? = context.contentResolver.openFileDescriptor(uri, "r")

第一个参数是我们刚刚得到的文件的uri,第二个文件是表示我们对文件的操作模式,我现在示范的是读取用户图片所以用只读模式("r")就可以了,关于mode的具体注释,这里我直接粘贴原文了

mode – The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw" or "rwt". SeeParcelFileDescriptor.parseMode for more details.

通过FileDescriptor可以打开一个文件IO流(FIS或者FOS),就可以读写文件啦

FileInputStream(pfd.fileDescriptor).use {
//这里可以先将用户的图片复制到私有目录中,再让用户做进一步的编辑操作
}
FileOutputStream(pfd.fileDescriptor).use {
}

但是注意,打开的fileDescriptor是Closeable对象,所以用完之后需要手动close(),这里我用的是ktolin的扩展函数,会在use代码块里的代码运行完之后自动关闭流

另一种读取文件的方法,还是使用contentResolver直接打开io流

context.contentResolver.openInputStream(uri)?.use {
}
context.contentResolver.openOutputStream(uri)?.use {
}

模拟将文件写入用户目录的操作

其实思路是一模一样的,只是你启动文件系统的意图(intent)不一样,以及对文件的操作不一样

我们需要拿到代表用户临时授权给APP的Uri

//这里传入你要创建的文件类型的mimeType,如果是"*/*"那就代表文件夹
val launcher = registerForActivityResult(ActivityResultContracts.CreateDocument("*/*")){uri->
    //这里获取到的uri是已经创建好的文件的uri
}
//这里传入要创建的文件名
launcher.launch("cache.png")

启动之后是这个界面

通过contentResolver打开文件的文件描述符FileDescriptor

val pfd : ParcelFileDescriptor? = context.contentResolver.openFileDescriptor(uri, "rw")

第一个参数是我们刚刚得到的文件的uri,第二个文件是表示我们对文件的操作模式,我现在示范的是保存一张图片所以要用读写模式("rw")

通过FileDescriptor可以打开一个文件IO流(FIS或者FOS),就可以写文件啦

FileOutputStream(pfd.fileDescriptor).use {
//这里将处理好的图片利用fos写到用户刚才用uri指定的地方
}

另一种读取文件的方法,还是使用contentResolver直接打开io流

context.contentResolver.openOutputStream(uri)?.use {
}

模拟获取用户文件夹控制权的操作

最后再模拟一下获取用户文件夹控制权的操作,通过这个方法你可以拿到其他应用在外置存储里的目录(例如一些聊天软件的聊天记录其实就是存放在这个目录的)

(先截了张图,过两天填坑)

通过Uri获取文件信息

最后再介绍一 通过Uri获取文件信息(文件名/文件大小/文件Mime类型)的方法

//第二个参数相当于是sql里的select,列表里是要过滤的列名,如果传null那说明取所有的列,这样性能会比较差
val cursor: Cursor? = context.contentResolver.query(
    this,
    arrayOf(MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.SIZE),
    null,
    null,
    null
)?.use { cursor ->
    if (cursor.moveToFirst()) {
        val columnIndex1 = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)
        if (columnIndex1 > -1) {
            name = cursor.getString(columnIndex1)
        }
        val columnIndex2 = cursor.getColumnIndex(MediaStore.MediaColumns.SIZE)
        if (columnIndex2 > -1) {
            size = cursor.getLong(columnIndex2)
        }
    }

文件的话,用正常途径也只能拿到文件名(MediaStore.MediaColumns.DISPLAY_NAME),文件大小(MediaStore.MediaColumns.SIZE),文件Mime类型(MediaStore.MediaColumns.MIME_TYPE)这三个有用的信息 注意,获取到的cursor是Closeable对象,所以用完之后需要手动close()

以上就是Android无需读写权限通过临时授权读写用户文件详解的详细内容,更多关于Android临时授权读写用户文件的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android使用DocumentFile读写外置存储的问题

    最近在维护项目,app遇到安装在高版本的Android时,以往直接授权和new File(path)的形式不再支持,日志也是说Permission denied.....好吧,换为DocumentFile. 经过一番操作,也终于实再对存储目录的读和写了,下面记录一下: 首先建一个DocumentFile的Utils类: import android.annotation.TargetApi; import android.content.Context; import android.conte

  • Android申请相机权限和读写权限实例

    开发一个相机应用,需要申请三个权限:相机.读文件.写文件. 1.在AndroidManifest.xml中添加 <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name

  • Android NDK开发(C语言-文件读写)

    目录 1.文件读写 1.1打开文件 1.2关闭文件 1.3读取文件 1.4写入文件 1.5读写二进制I/O文件 1.6获取文件的大小 1.7文本简单加密.解密 1.8二进制文件简单加解密 1.文件读写 一个文件,无论它是文本文件还是二进制文件,都是代表了一系列的字节.C 语言不仅提供了访问顶层的函数,也提供了底层(OS)调用来处理存储设备上的文件. 1.1打开文件 我们可以使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FI

  • Android11及以上文件读写权限申请详细介绍

    目录 Android11 读写权限申请 AndroidManifest添加权限设置 代码申请动态权限 Android11申请权限 补充:Android 11 及以上申请外部存储权限 总结 Android11 读写权限申请 Android11系统对应用写入权限做了严格的限制.本文介绍如何获取文件读写权限.项目中 build.gradle 的targetSdkVersion >= 29 ,会出现读写问题. 当targetSdkVersion = 29,通过设置requestLegacyExterna

  • android中NFC读写功能的实现方法

    本文实例为大家分享了android中NFC读写功能的具体代码,供大家参考,具体内容如下 首先检查一下设备是否支持NFC功能 private void checkNFCFunction() { // TODO Auto-generated method stub mNfcAdapter = NfcAdapter.getDefaultAdapter(this); // check the NFC adapter first if (mNfcAdapter == null) { // mTextVie

  • Android实现读写USB串口数据

    本文实例为大家分享了Android实现读写USB串口数据的具体代码,供大家参考,具体内容如下 最近在研究USB方面的内容:先后做了关于Android读写HID.串口设备的DEMO.本文比较简单,主要介绍的是Android实现读取串口数据的功能 废话不多说,先看一下业务层是如何调用的:如图: 首先,监听USB连接状况,当USB 进行请求USB权限,当USB权限申请成功,进行调用打开Usb设备的方法:当监听到USB断开,进行关闭连接: 这是向串口写入数据的方法: 本DEMO主要使用Handle进行数

  • Android无需申请权限拨打电话的两种方式

    Android打电话有两种实现方法: 第一种方法,拨打电话跳转到拨号界面.源代码如下: Intent intent = new Intent(Intent.ACTION_DIAL); Uri data = Uri.parse("tel:" + "135xxxxxxxx"); intent.setData(data); startActivity(intent); 第二种方法,拨打电话直接进行拨打,但是有些第三方rom(例如:MIUI),不会直接进行拨打,而是要用户进

  • Android开发之保存图片到相册的三种方法详解

    目录 方法一 方法二 方法三 有三种方法如下:三个方法都需要动态申请读写权限否则保存图片到相册也会失败 方法一 /** * 保存bitmap到本地 * * @param bitmap Bitmap */ public static void saveBitmap(Bitmap bitmap, String path) { String savePath; File filePic; if (Environment.getExternalStorageState().equals(Environm

  • Android中用Bmob实现短信验证码功能的方法详解

    这篇文章主要介绍发送验证码和校验验证码的功能,用到一个第三方平台Bmob,那Bmob是什么呢?Bmob可以开发一个云存储的移动应用软件,他提供了大量的标准的API接口,根据需要接入相关服务,开发者可以更加专注于应用的开发,让产品交付更快速,验证码功能就是其中一个. 一.跟其他第三方一样,我们开发之前要做一些准备工作. 1.首先,去官网注册一个帐号:http://www.bmob.cn/: 2.然后就可以创建应用了:具体怎么做Bmob说得很清楚了(官方操作介绍),如果你不想看,我简单说一下:点击右

  • Android 中Manifest.xml文件详解

    Android 中Manifest.xml文件详解 每一个Android项目都包含一个清单(Manifest)文件--AndroidManifest.xml,它存储在项目层次中的最底层.清单可以定义应用程序及其组件的结构和元数据. 它包含了组成应用程序的每一个组件(活动.服务.内容提供器和广播接收器)的节点,并使用Intent过滤器和权限来确定这些组件之间以及这些组件和其他应用程序是如何交互的. 它还提供了各种属性来详细地说明应用程序的元数据(如它的图标或者主题)以及额外的可用来进行安全设置和单

  • Android init.rc文件详解及简单实例

    Android init.rc文件详解 本文主要来自$ANDROID_SOURCE/system/init/readme.txt的翻译. 1 简述 Android init.rc文件由系统第一个启动的init程序解析,此文件由语句组成,主要包含了四种类型的语句:Action,Commands,Services,Options.在init.rc文件中一条语句通常是占据一行.单词之间是通过空格符来相隔的.如果需要在单词内使用空格,那么得使用转义字符"\",如果在一行的末尾有一个反斜杠,那么

  • Android异步下载图片并且缓存图片到本地DEMO详解

    在Android开发中我们经常有这样的需求,从服务器上下载xml或者JSON类型的数据,其中包括一些图片资源,本demo模拟了这个需求,从网络上加载XML资源,其中包括图片,我们要做的解析XML里面的数据,并且把图片缓存到本地一个cache目录里面,并且用一个自定义的Adapter去填充到LIstView,demo运行效果见下图: 通过这个demo,要学会有一下几点 1.怎么解析一个XML 2.demo中用到的缓存图片到本地一个临时目录的思想是怎样的? 3.AsyncTask类的使用,因为要去异

  • Android利用Dom对XML进行增删改查操作详解

    1. 概述 平常我们一般是使用JSON与服务器做数据通信,JSON的话,直接用GSON或者其他库去解析很简单.但是,其他有些服务器会返回XML格式的文件,这时候就需要去读取XML文件了. XML的解析有三种方式,在Android中提供了三种解析XML的方式:DOM(Document Objrect Model) , SAX(Simple API XML) ,以及Android推荐的Pull解析方式,他们也各有弊端,而这里来看看使用DOM的方式. 2. Dom解析 DOM解析器在解析XML文档时,

  • android 9.0 launcher3 去掉抽屉式显示所有 app(代码详解)

    效果图 修改思路 1.增加全局控制变量 sys.launcher3.is_full_app,用来动态切换 2.增加两套布局,对应有抽屉和无抽屉 3.去除 allAppsButton 4.将 AllAppsContainerView 中的图标加载到 Workspace 5.新安装的 app 自动添加图标到 Workspace 6.替换 Workspace 图标长按删除选项为取消 7.屏蔽上拉显示抽屉页面手势 8.修改页面指示线为圆点 上代码 1.增加全局控制变量 sys.launcher3.is_

  • Android获取arrays.xml里的数组字段值实例详解

    Android获取arrays.xml里的数组字段值实例详解 比如在arrays.xml里: <!--leo added for KYLIN-496--> <string-array name="reboot_item"> <item>Reboot</item> <item>Recovery</item> <item>BootLoader</item> </string-array&g

  • Android中Spinner(下拉框)控件的使用详解

    android给我们提供了一个spinner控件,这个控件主要就是一个列表,那么我们就来说说这个控件吧,这个控件在以前的也看见过,但今天还是从新介绍一遍吧. Spinner位于 android.widget包下,每次只显示用户选中的元素,当用户再次点击时,会弹出选择列表供用户选择,而选择列表中的元素同样来自适配器.Spinner是View类得一个子类. 1.效果图 2.创建页面文件(main.xml) <Spinner android:id="@+id/spinner1" and

随机推荐