从零开始开发纯血鸿蒙应用之处理外部文件
从零开始开发纯血鸿蒙应用
- 一、外部文件
- 二、外部文件的访问形式
- 1、主动访问
- 2、被动访问
- 三、代码实现
- 1、DocumentViewPicker
- 2、Ability Skills
- 3、onNewWant 函数
- 4、冷启动时处理外部文件
一、外部文件
对于移动端app来说,什么是外部文件呢?是那些存储在手机之外的存储介质中的文件吗?还是另有说法呢?
不同于桌面端的应用,手机操作系统对文件的访问控制非常严格,但app读写文件又是非常基础的需要,所以,像华为的鸿蒙系统以及苹果手机的iOS系统,都专门为app开辟了一个名为沙箱目录的目录区域,每个app都有自己专属的一个沙箱目录。
app在自己的沙箱目录里,可以很自由的进行文件的创建和读写、以及删除。对于存放在自己的沙箱目录中的文件,我称之为app的内部文件,而那些存储在自己的沙箱目录外的文件,或是存储在用户目录的文件,也或是存储在其他app的沙箱目录中的文件,我就称之为外部文件。
需要提醒的是,app的沙箱目录,在手机上用文件管理是看不到的,必须借助电脑上的专门工具,例如 DevEco Studio 才能看到。app 的沙箱目录一般路径为 /data/app/el2/100/base/[package name]/hap/entry
。
对于 DevEco Studio 提供的 Device File Browser,我想多说两句,一是该工具提供了两种查看视图,普通文件视图和沙箱文件视图,第二种视图依赖于应用需要在 module.josn5 中开启 ohos.permission.INTERNET
权限;二是该工具并不是所有app的沙箱目录都可以查看:
二、外部文件的访问形式
外部文件的访问,主要有两种形式:主动访问和被动访问。
1、主动访问
所谓主动访问,就是app自己通过主动调用鸿蒙 API——picker,拉起文件选择面板,让用户去选择文件,然后再对目标文件进行内容读取和处理。
picker 会将选中的目标文件的File URI
暴露出来,通过该File URI,就可以按图索骥进行文件操作,比如读取文件属性信息。
2、被动访问
如上图的这种操作,我相信大家平日的手机使用过程中,都会遇到。当一个文件不是当前app能够进行处理的时候,往往系统就会让你选中用其他应用去处理,实际上,这种方式就是让你选择的那个目标应用被动地进行外部文件的访问和处理。
在鸿蒙系统中,一个app默认是不具有被动处理外部文件的能力的,想要具备这种能力,需要app自己向系统声明,一般就是在 module.json5 文件中进行相关配置。
三、代码实现
1、DocumentViewPicker
鸿蒙系统针对不同的文件类型,提供了多种具体的 picker,其中,针对文件的就是这里即将进行介绍的DocumentViewPicker
。
DocumentViewPicker 的使用非常简单,只需传入一个 option 即可:
const option: picker.DocumentSelectOptions = new picker.DocumentSelectOptions();
option.maxSelectNumber = 1;
option.fileSuffixFilters = ['.txt', '.log', '.csv', '.ini', '.conf', '.md', '.markdown', '.rtf', '.json',
'.xml', '.ets', '.java', '.py',
'.c', '.cpp', '.h', '.html']
const filePicker = new picker.DocumentViewPicker(this.ctx);
filePicker.select(option).then((uri) => {
Logger.info(`选中的文件:${uri}`, TAG);
const param: StrRouterParam = new StrRouterParam(uri[0]);
router.pushUrl({
url: "pages/ImportFilePage",
params: param
})
})
过程步骤可以描述为如下的几步:
1)创建一个 picker.DocumentSelectOptions
对象
2)使用 picker.DocumentSelectOptions 对象的属性,设置最大选中数、目标文件后缀等必要的文件过滤条件。picker.DocumentSelectOptions 对象一共有如下属性:
- maxSelectNumber:最大可选中数,值域范围1~500,如果是选择目录,那么该值只能为1;默认值为 1.
- defaultFilePathUri:指定选择文件或目录路径,即默认的文件或目录路径;没有默认值。
- fileSuffixFilters:目标文件的后缀;默认全部文件。
- selectMode:选择模式,用于设置支持选择的资源类型:文件、文件夹或二者混合;默认是文件。
- authMode:授权模式,默认为 false;当值为 true,则 defaultFilePathUri 必填,表示待授权的 uri。
3)利用 UI 上下文,创建一个 picker.DocumentViewPicker 对象,并使用该对象的 select 方法拉起文件选择面板,而select 方法的回调处理中,可以获得用户选中的文件的 uri,我这里是将该 uri 通过 router 路由携带跳转到专门用展示外部文件的页面里,然后展示文件内容:
2、Ability Skills
前面说了,应用默认是不具有被动处理外部文件的能力的,除非向系统声明了,而这个声明就是在 module.json5 中进行能力声明:
skills 标签里的 actions 标签,用于声明具体的能力动作,上面的ohos.want.action.viewData
,就表示查看数据的操作;同样位于 skills 标签里面的 uris 标签,可以为 viewData 动作进行进一步的细化,声明支持何种类型的数据的查看,如上的配置,则表示支持通用纯文本文件类型的数据的查看。
通过类似如上的配置,当用户需要进入“使用其他应用打开文件”的场景时,系统就会判断待打开的文件,是否与应用在 module.json5 文件中声明的数据查看能力相符合,如果符合就会展示在应用候选中,否则就不展示。
3、onNewWant 函数
光在 module.json5 文件中声明 Ability Skills,尚不足以形成真正的处理外部文件的能力,因为传记录的文件uri 没有任何代码进行读取等文件操作,而想要实现这一步骤,就需要用到一个应用生命周期函数 onNewWant 函数。
当应用已经完成冷启动并进入后台运行状态时,用户的“选择某某app打开文件”的操作,就会触发该生命周期函数,而函数的 Want 参数里面,就包含了用户想要打开的文件的 File Uri,因此,就可以用类似如下的代码进行处理:
4、冷启动时处理外部文件
用户在其他app页面,选择用当前应用去打开文件时,当前应用可能并未完成冷启动,这时候,就不会触发 OnNewWant 函数,而是触发 onCreate 函数:
然而,由于 onCreate 函数早于 onWindowStageCreate 的执行,意味着 UI 进程还没开始,也就是页面跳转动作还不能进行,所以,在 onCreate 函数里面处理其他app传进来的文件,是不合适的,不妨移步到 onWindowStageCreate 函数中。
onWindowStageCreate 函数的参数,虽然没有 Want 对象,但是 this 上下文里面就有有一个 launchWant
实例,借助它依旧能够获取到其他app传入的文件 uri,由于 windowStage.loadContent
不像 router.push 那样携带参数,而 windowStage.loadContent
在被执行完成之前,应用主页未被渲染出来,router.push 依旧用不了,所以,我利用 AppStorage
去完成外部文件 uri 的透传:
在应用主页的 aboutToAppear 生命周期函数中,判断 AppStorage 中记录的 SignalConstants.SIGNAL_OUTFILE_URI
是否为非空字符串,如果是,则将对应的外部文件的 File Uri 通过 router.push 携带跳转到 ViewOutsideFile 页面,从而完成冷启动状态下对外部文件的处理。