Android系统编程入门系列之应用级文件在应用程序间的共享
在上篇文章了解到应用级文件只能被其所创建的应用程序所访问,那么其他应用程序是不是就无论如何都无法访问了呢?肯定不是的,只要文件经过其创建的应用程序授权,还是可以被其他应用程序所访问的。这也就是应用级文件的共享。
系统只允许共享包含实际数据的纯文件类型,而不推荐共享包含文件的目录类型。
对于文件的访问可以使用java.io.File系统文件类,但是如果想将该文件分享出去,则需要借助android.net.Uri路径定位符类。Uri
类是Android系统下自定义的路径定位符规则,其符合Java中定义的java.net.URI标准资源定位符规范,并新增了getPathSegments()
获取分割路径列表的方法和getQueryParameter(String key)
获取指定的额外参数方法等。
通常思路是将要分享的File
文件对象转换成特定的Uri
路径定位符对象。在Android7即API 24版本及以后的版本中,系统对Uri
增加了授权处理,这将在后文详细解释。
之后将该Uri
对象可以作android.os.Bundle数据对象通过android.content.Intent意图类传递。
最终在接收到意图的应用程序中,可以收到Uri
路径并对其做不同处理,即可访问该路径下的文件,针对Uri
中的内容不同,一般需要做不同的转换处理,这将在后文详细解释。
文件分享方
目标版本级别小于24的应用程序
在Android7即API 24版本以前,应用程序内的任何File
对象,都可以通过静态方法Uri.fromFile(File file)
将参数 file 直接转换为Uri
对象。
得到的Uri
对象可直接操作赋值给Intent
意图对象的setData(Uri data)
方法中的参数 data ,或putExtra(String key, Parcelable value)
方法中的参数 value,从而分享传递出去。
目标版本级别大于等于24的应用程序
从Android7即API 24版本开始,应用程序内部共享文件,仍然可以使用Uri.fromFile(File file)
方法,但是如果想在应用程序中对得到的Uri
对象直接传递给其他应用程序使用,会抛出android.os.FileUriExposedException异常。因此,在使用androidx依赖包的应用程序中可以借助androidx.core.content.FileProvider文件分享提供类,在使用support-v4依赖包的应用程序中可以借助android.support.v4.content.FileProvider
文件分享提供类,获取相关Uri
对象,并为其授权,之后才可在不同应用程序间共享访问。
对于FileProvider
的使用,要先做准备工作。
首先需要在应用程序的清单文件中注册。在<application></application>
标签中增加<provider></provider>
标签。
在该标签中指定属性android:name="androidx.core.content.FileProvider"
以绑定文件分享提供类;
另外在该标签中指定属性android:authorities="xxx"
,这里的属性值xxx
是应用程序所在系统内唯一的,其定义规则通常建议以应用程序包名为前缀的域名,且在后文代码中有使用;
同时在改标签中指定属性android:grantUriPermissions="true"
,属性值为 true 时,表示允许对得到的Uri
所定位的File
文件授予临时读写权限。
最后在改标签中增加<meta-data/>
标签,以指定所使用的数据信息。在该数据标签中,必须定义其属性为android:name="android.support.FILE_PROVIDER_PATHS"
,同时定义属性android:resource="@xml/file_paths"
,这里的属性值@xml/file_paths
是指向定义的资源文件file_paths.xml。
在自定义的资源目录 res 下创建子目录 xml,该目录中创建文件 file_paths.xml 。定义<paths></paths>
根标签以指定要分享的文件路径。在根标签中可以根据要分享的文件所属路径不同而使用不同类型的标签名作为子标签插入,子标签中包含有name
和path
两个属性,分别设置该路径的别名和子目录。其中子标签可用类型与代码中获取路径的对应关系可参考下表。
代码获取路径 | 子标签名 |
---|---|
context.getFilesDir() |
<files-path> |
context.getCacheDir() |
<cache-path> |
Environment.getExternalStorageDirectory() |
<external-path> |
context.getExternalMediaDirs() |
<external-media-path> |
context.getExternalFilesDir() |
<external-file-path> |
context.getExternalCacheDir() |
<external-cache-path> |
在清单文件中注册FileProvider
之后,就可以在代码获取Uri
的地方,与低版本中获取Uri
的方法Uri.fromFile(File file)
所不同的是,替换为FileProvider
的静态方法getUriForFile(Context context, String authority, File file)
。参数 context 是当前应用程序所在的上下文环境;参数 authority 则是在清单文件中注册FileProvider
时,属性android:authorities
所表示的值;参数 file
依然是应用程序中要分享的文件对象。
在使用FileProvider.getUriForFile(Context context, String authority, File file)
获取到Uri
之后,需要授权给要使用该路径的应用程序,在能获取到上下文环境Context
对象的地方,调用grantUriPermission (String toPackage, Uri uri, int modeFlags)
方法授权。参数 toPackage 即为要授权使用该路径的应用程序所在包名;参数 uri 是上文获取到的路径定位符对象;参数 modeFlags 是用来表示权限类型的标志位,其值目前包括四种:Intent.FLAG_GRANT_READ_URI_PERMISSION=1
的读权限,Intent.FLAG_GRANT_WRITE_URI_PERMISSION=2
的写权限,Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION=4
表示长久授权,和Intent.FLAG_GRANT_PREFIX_URI_PERMISSION=8
对匹配参数 uri 的前缀的所有路径定位符均授权。
在对Uri
对象授权之后,也就可以像老版本一样将其封装到Intent
意图对象中发送给其他应用了。
文件接收方
一般地,在分享文件的接收方,如果只是对接收到的文件内容做读写处理,可以借助java.io.FileDescriptor文件描述类,使用官方推荐的简单方式操作。
在接收方收到的Intent
意图对象调用getData()
方法,得到要接收的文件所对应的Uri
路径定位符对象。
在可以使用上下文环境Context
对象的位置,调用其getContentResolver()
方法,获取上下文环境中的ContentResolver
内容解析类对象。
借助ContentResolver
对象的openFileDescriptor (Uri uri, String mode)
方法,返回android.os.ParcelFileDescriptor系统封装的文件描述类对象。其中参数 uri 就是上文接收到的路径定位符对象;参数 mode 则标记了文件的打开方式,包括只读权限"r",只写权限"w",追加写入权限"wa",读写权限"rw"等。
在得到ParcelFileDescriptor
对象后,调用其getFileDescriptor()
方法,可以获得FileDescriptor
文件描述对象。
最终通过得到的文件描述对象,创建FileInputStream(FileDescriptor fdObj)
构造方法或FileOutputStream(FileDescriptor fdObj)
构造方法获取文件输入流对象或文件输出流对象,以此操作文件内容。
还有些特殊需求,对于分享文件接收方来说,是要对收到的文件获取其所在目录并做进一步操作,也就是需要对得到的Uri
路径定位符对象转换为File
文件对象。这方面官方并没有像上文那样简单统一的调用方式,而需要开发者针对不同的版本做单独处理。
目标版本级别小于29的应用程序
在Android10即API 29之前的版本中,网上有一堆代码可参考,其思路就是查询系统数据库中不同媒体文件是否与Uri
对象所指向位置匹配,进而确定该Uri
对象指向的媒体文件目录,在应用程序内借助上下文环境获取其媒体文件所在目录File
对象,从而匹配要查询的Uri
路径定位符对象。
目标版本级别大于等于29的应用程序
从Android10及API 29版本开始,由于应用程序仅可访问自己分区存储内部的文件,对于收到的分享文件路径定位符Uri
对象,也只能先使用上文官方推荐的读取文件方式,将Uri
对象所指向的文件先保存一份到自己应用程序内部的私有目录下,再将这份私有目录下的保存文件转为File
对象使用。
Android系统编程入门系列之应用级文件在应用程序间的共享的更多相关文章
- Android系统编程入门系列之应用数据文件化保存
应用中关于数据的持久化保存,不管是简单的SharedPreferences还是数据库SQLiteDatabase,本质上都是将数据保存到系统的某种类型的文件中.因此可以直接使用java.io.File ...
- Android系统编程入门系列之加载界面Activity
上回说到应用初始化加载及其生命周期,在Android系统调用Applicaiton.onCreate()之后,继续创建并加载清单文件中注册的首个界面即主Activity,也可称之为入口界面.主Acti ...
- Android系统编程入门系列之应用初始化Application
在上一篇文章中我们了解到Android系统启动应用的时候,会首先加载AndroidManifest.xml清单文件中的一系列信息,在清单文件中如果不指定<application></ ...
- Android系统编程入门系列之清单文件
在上一篇文章中已经提到,Android系统加载应用程序之后,首先会读取该应用程序的AndroidManifest.xml清单文件,之后根据该清单文件加载后边的东西.所以要开发应用程序,自然要先知道清单 ...
- Android系统编程入门系列之应用环境及开发环境介绍
作为移动端操作系统,目前最新的Android 11.0已经发展的比较完善了,现在也到了系统的整理一番的时间,接下来的系列文章将以Android开发者为中心,争取用归纳总结的态度对初级入门者所应 ...
- Android系统编程入门系列之应用内键值对数据的简单保存
在应用程序间及与用户的通信交互过程中,会产生并传递一系列数据.针对这些数据,有部分是只在应用程序中使用的缓存数据,还有一部分是在不同位置多次或长时间使用的持久化数据. 对于缓存数据来说,通常以代码中定 ...
- Android系统编程入门系列之应用内数据保存数据库
上篇文章已经介绍了如何使用SharedPreferences存储键值对形式的轻量级数据,对于那些相同结构的多组数据,类似于存储Java中定义的类的多个对象属性值,如果按照键值对的形式一条条读写,需要分 ...
- Android系统编程入门系列之界面Activity绘制展示
上篇文章介绍了界面Activity的启动方式和生命周期,本篇将继续介绍在界面Activity中的内容是如何绘制展示给用户的. 在Android系统上运行新创建的界面Activtiy,给用户展示的是空白 ...
- Android系统编程入门系列之界面Activity交互响应
在上篇文章中已经了解到界面Activity的绘制完全依赖其加载的视图组件View,不仅如此,用户的每次触摸操作都可以在界面Activity内接收并响应,也可以直接传递给其中的某个视图View响应.本文 ...
随机推荐
- Mysql慢查询explain
转自:https://www.toutiao.com/i6776461352522220036/?tt_from=weixin&utm_campaign=client_share&wx ...
- 华为音频编辑服务(Audio Editor Kit),快速构建应用音频编辑能力
音频编辑服务(Audio Editor Kit)是华为为开发者开放的各类场景音频处理能力的集合,汇聚了华为在音乐.语音等相关音频领域的先进技术.音频编辑服务提供基础编辑.伴奏提取.空间渲染.变声降噪等 ...
- freeswitch新增模块API
概述 上一章我们讲解了freeswitch的源码基本结构,以及如何新增一个插件式模块. freeswitch的架构非常适合这种业务开发模式,即以freeswitch的基本功能为开发平台,新增插件式模块 ...
- 使用spring向service里面注入dao不成功。
因为原来的程序没有使用spring.后来加spring的时候action有个地方的new没有改!!! new了个新的实现层 不是spring管理的对象.
- Linux下修改mysql默认最大连接数
liunx下修改mysql最大连接数(Centos下测试通过)1.查看当前系统下mysql设置的最大连接数方式一.mysqladmin -uroot -p variables |grep max_co ...
- 手机端rem简单配置相关
手机端rem简单配置相关 1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 ...
- Qt中QOpengl的QMatrix4x4矩阵作用原理以及使用方法
1.矩阵具有坐标变换的作用,例如:左乘一个旋转矩阵,实现点的坐标旋转,左乘一个平移矩阵实现,点的平移 2.一个点可以同时串联相乘几个变换矩阵,实现坐标连续变换,根据左乘规则,右边矩阵先作用于点,作用顺 ...
- SQL语句之高级使用
1.select top select top 用于规定要返回的数据的数目 注意:并非所有的数据库系统都支持 SELECT TOP 语句. MySQL 支持 LIMIT 语句来选取指定的条数数据, ...
- SAR总结
1.星载InSAR技术简介 星载合成孔径雷达干涉测量(InSAR)是一种用于大地测量和遥感的雷达技术.InSAR使用两个或多个SAR图像,利用返回卫星的波的相位差来计算目标地区的地形.地貌以及表面的微 ...
- C# Dapper基本三层架构使用 (四、Web UI层)
三层架构的好处,一套代码无论WinForm还是Web都可以通用,只写前台逻辑就可以了,现在展示Web调用三层的示例 首先在项目中创建一个Web MVC5项目,目前项目目录如下 在Web项目Web.co ...