引言
在很多应用场景中,用户需要快速查看一个文件的内容——可能是一份文档、一张图片、一段视频,或者一个纯文本文件。用户的预期很简单:点一下就能看,不需要专门打开另一个编辑器。
鸿蒙的 Preview Kit 提供的就是这种能力。它以拉起新窗口的方式展示文件内容,界面统一、使用简单,开发者不需要自己去实现各类文件格式的渲染逻辑。此外,从 5.0.5(17) 版本开始,Preview Kit 还引入了文件预加载状态感知的能力,让浏览器等下载类应用可以提前感知文件是否已被系统预加载,从而给用户更明确的"加速打开"提示。
本文面向有一定鸿蒙开发基础的开发者,梳理 Preview Kit 的使用方式、开发流程中的关键环节,以及文件预加载状态感知的接入方法。
一、Preview Kit 能做什么,不能做什么
在动手之前,先厘清 Preview Kit 当前的能力边界,避免走弯路。
能做的事:
- 拉起一个独立的预览窗口,以统一的界面展示文件内容。
- 支持图片、视频、音频、文本、HTML 等常见文件类型的预览。
- Office 类文档(如 .docx、.xlsx 等)借助 WPS 的能力来渲染,所以预览界面中会出现"WPS 提供技术支持"等字样——这是正常的。
- 支持单文件预览和多文件预览(多文件仅限移动端)。
- 预览窗口是单例的,同一时间只会存在一个。
当前的限制:
- 只支持跳出应用预览,暂不支持在应用内嵌入预览视图。如果你想在应用页面里直接内嵌一个文件预览区域,目前 Preview Kit 还做不到。
- 没有安全定制能力。禁止截录屏、屏蔽"使用其他应用打开"、屏蔽分享入口等安全相关的控制,暂时都不支持。
- 需要文件的访问权限。调用方必须具备对应 URI 的转授权能力,让预览窗口能正常读取文件内容。这一点在实际开发中容易踩坑,如果预览时报错,优先检查权限问题。
二、文件预览的完整开发流程
Preview Kit 的使用流程可以归纳为:判断能否预览 → 打开预览 → (可选)切换文件 → (可选)关闭预览。下面逐步展开。
2.1 导入模块
import { filePreview } from '@kit.PreviewKit';
import { BusinessError } from '@kit.BasicServicesKit';
filePreview 是 Preview Kit 的核心模块,BusinessError 用于错误处理。
2.2 先判断文件能不能预览
在正式打开预览之前,最好先检查一下目标文件是否支持预览。这一步不是必须的,但可以帮你在 UI 层做更好的引导——比如对不支持预览的文件,显示"不支持预览"的提示而不是让用户点了没反应。
let uri = 'file://docs/storage/Users/currentUser/Documents/1.txt';
let uiContext = this.getUIContext().getHostContext() as Context;
filePreview.canPreview(uiContext, uri).then((result) => {
console.info(`是否可预览: ${result}`);
}).catch((err: BusinessError) => {
console.error(`判断失败, err.code = ${err.code}, err.message = ${err.message}`);
});
当传入的文件类型在支持范围内(图片、视频、音频、文本、HTML)且文件确实存在时,返回 true;否则返回 false。
2.3 打开预览窗口
这是最核心的一步。Preview Kit 提供了两种异步调用风格——Promise 和 Callback,功能完全一样,选择你习惯的即可。
Promise 方式:
let uiContext = this.getUIContext().getHostContext() as Context;
let displayInfo: filePreview.DisplayInfo = {
x: 100, y: 100,
width: 800, height: 800
};
let fileInfo: filePreview.PreviewInfo = {
title: '1.txt',
uri: 'file://docs/storage/Users/currentUser/Documents/1.txt',
mimeType: 'text/plain'
};
filePreview.openPreview(uiContext, fileInfo, displayInfo).then(() => {
console.info('预览打开成功');
}).catch((err: BusinessError) => {
console.error(`打开失败, err.code = ${err.code}, err.message = ${err.message}`);
});
这里有几个关键参数需要说明:
PreviewInfo:描述要预览的文件,包含标题(title)、文件路径(uri)和 MIME 类型(mimeType)。MIME 类型告诉系统这是什么格式的文件,以便选择正确的渲染方式。DisplayInfo:控制预览窗口的位置和大小,包括 x/y 坐标以及宽高。
有一个细节值得注意:1 秒内重复调用 openPreview 是无效的。这是为了防止用户快速连续点击导致重复打开窗口,开发者不需要自己加防抖逻辑。
Callback 方式:
filePreview.openPreview(uiContext, fileInfo, displayInfo, (err) => {
if (err && err.code) {
console.error(`打开失败, err.code = ${err.code}, err.message = ${err.message}`);
return;
}
console.info('预览打开成功');
});
2.4 一次预览多个文件(仅移动端)
在移动端场景下,你可以一次传入多个文件,用户可以在预览窗口中左右滑动切换。传入时通过 index 参数指定默认展示第几个文件(从 0 开始):
let files: Array<filePreview.PreviewInfo> = [];
files.push({
title: '1.txt',
uri: 'file://docs/storage/Users/currentUser/Documents/1.txt',
mimeType: 'text/plain'
});
files.push({
title: '2.txt',
uri: 'file://docs/storage/Users/currentUser/Documents/2.txt',
mimeType: 'text/plain'
});
filePreview.openPreview(uiContext, files, 0).then(() => {
console.info('多文件预览打开成功');
}).catch((err: BusinessError) => {
console.error(`打开失败, err.code = ${err.code}, err.message = ${err.message}`);
});
这个能力比较适合文件管理器、聊天应用中查看多张图片等场景。
2.5 在已有窗口中切换文件
如果预览窗口已经打开了,你不需要关闭再重新打开来展示另一个文件。loadData 方法可以直接在当前窗口中加载新的文件内容:
let newFile: filePreview.PreviewInfo = {
title: '2.txt',
uri: 'file://docs/storage/Users/currentUser/Documents/2.txt',
mimeType: 'text/plain'
};
filePreview.loadData(uiContext, newFile).then(() => {
console.info('文件加载成功');
}).catch((err: BusinessError) => {
console.error(`加载失败, err.code = ${err.code}, err.message = ${err.message}`);
});
需要注意的是,loadData 只在预览窗口已经存在时才生效,而且 100 毫秒内重复调用无效(比 openPreview 的 1 秒限制更短,因为切换文件的操作相对轻量)。如果传入的文件不支持预览,窗口会显示"不支持预览"的界面,而不是报错。
2.6 关闭预览窗口
filePreview.closePreview(uiContext).then(() => {
console.info('预览窗口已关闭');
}).catch((err: BusinessError) => {
console.error(`关闭失败, err.code = ${err.code}, err.message = ${err.message}`);
});
同样只在预览窗口存在时生效。另外,你还可以通过 hasDisplayed 方法来判断预览窗口当前是否处于打开状态,在一些需要条件判断的逻辑中会比较有用。
三、文件预加载状态感知:让"秒开"看得见
从 5.0.5(17) 版本开始,Preview Kit 新增了一项面向特定场景的能力——文件预加载状态感知。这个功能目前仅在 2in1 设备上支持,典型的使用者是浏览器等支持下载文件的应用。
3.1 这项能力解决什么问题
系统有时会对用户可能打开的文件进行预加载,也就是提前把文件内容读入缓存,让后续打开更快。但问题在于,用户并不知道哪些文件已经被预加载了。如果应用能感知到某个文件的预加载状态,就可以在 UI 上给用户一个明确的提示——比如一个"闪电"图标表示"这个文件可以秒开",体验上会好很多。
3.2 三种预加载状态
一个文件的预加载状态有三种:
| 状态 | 含义 | 建议的 UI 处理 |
|---|---|---|
PRELOADING | 正在预加载中 | 可以显示加载动画 |
PRELOADED | 预加载已完成 | 提示用户"加速打开"已就绪 |
NOT_PRELOADED | 没有预加载 | 不需要额外提示 |
3.3 开发流程
整个接入过程分为四步:注册回调 → 添加文件监听 → 响应状态变化 → 清理资源。
第一步,导入模块并注册回调:
import { openFileBoost } from '@kit.PreviewKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
function callback(info: openFileBoost.FilePreloadStatusInfo): void {
if (info.state === openFileBoost.FilePreloadState.PRELOADING) {
hilog.info(0x0000, 'testTag', '文件正在预加载,可以展示加载动画');
}
if (info.state === openFileBoost.FilePreloadState.PRELOADED) {
hilog.info(0x0000, 'testTag', '预加载完成,可以提示用户加速打开');
}
if (info.state === openFileBoost.FilePreloadState.NOT_PRELOADED) {
hilog.info(0x0000, 'testTag', '未预加载,无需额外提示');
}
}
// 注册监听
openFileBoost.on('filePreloadStateChanged', callback);
第二步,添加需要监听的文件:
注册回调之后,通过 addFile 传入文件的沙箱路径。之后这个文件的预加载状态一旦变化,就会通过上面注册的回调通知你。
const file = "/storage/Users/currentUser/Desktop/10MB_file.docx";
openFileBoost.addFile(file);
一个典型场景是:用户通过浏览器下载了一个文件,下载完成后,应用将该文件路径注册进来,后续如果系统对其进行了预加载,应用就能及时在下载列表中标记出来。
这里有一个限制需要注意:单个应用最多同时监听 50 个文件。不再需要关注的文件,应该及时调用 removeFile 取消监听,把名额腾出来:
openFileBoost.removeFile(file);
第三步,主动查询预加载状态:
除了被动等回调,你也可以主动查询某个文件当前的预加载状态。这在应用刚启动时特别有用——可以遍历一遍关心的文件,把已经预加载完成的标记出来:
let statusInfo = openFileBoost.queryFilePreloadStatusInfo(file);
hilog.info(0x0000, 'testTag',
`文件: ${statusInfo.sandboxPath}, 进度: ${statusInfo.progress}, 状态: ${statusInfo.state}`);
返回的 FilePreloadStatusInfo 不仅包含状态,还有加载进度信息,可以用于更细粒度的 UI 展示。
第四步,清理资源:
当不再需要监听任何文件的预加载状态时,注销回调。off 方法支持两种用法:传入具体的回调函数则只取消该函数的监听,不传参数则取消当前进程的所有回调。
// 只取消某一个回调
openFileBoost.off('filePreloadStateChanged', callback);
// 取消所有回调
openFileBoost.off('filePreloadStateChanged');
3.4 接入前的准备
因为这项能力目前仅在 2in1 设备上可用,在接入之前,建议先通过 Syscap 查询目标设备是否支持 SystemCapability.PCService.OpenFileBoost。对于不支持的设备类型(如手机、平板),不需要也不应该调用这些接口。
另外,当前支持预加载的文件类型是有限的,不在支持范围内的文件类型默认为"未预加载"状态,不需要额外注册监听。
四、总结与实践建议
Preview Kit 的两项能力——文件预览和预加载状态感知——分别解决的是"怎么看文件"和"怎么更快地看文件"的问题。
对于文件预览,开发者要抓住几个要点:
- 权限先行。预览的前提是文件可被访问,URI 的转授权是最常见的问题来源。
- 善用
canPreview做前置判断。在 UI 层提前给用户预期,好过让用户点击后看到失败。 - 预览窗口是单例的。不需要担心重复打开的问题,但切换文件时记得用
loadData而不是重新调用openPreview。 - 留意调用频率限制。
openPreview有 1 秒的防重复机制,loadData是 100 毫秒。
对于预加载状态感知,如果你的应用运行在 2in1 设备上且涉及文件下载或管理,这是一个值得接入的体验增强点。用户能直观看到哪些文件可以"秒开",点击的信心和满意度都会更高。接入时注意控制监听文件数量(上限 50 个),及时清理不再需要的监听,保持资源使用的整洁。