接入指南
第三方快应用如何接入 SimpleFetch 协议,通过手机桥接发起 HTTP / SSE 请求
本指南面向快应用开发者,将指导你把自己的快应用接入 SimpleFetch 以实现联网。
通信协议的字段级定义见 通信协议,本文仅介绍如何接入 SimpleFetch。
它是怎么工作的
你的快应用 ──互联消息(SF_*)──▶ SimpleFetch 插件 ──真实 HTTP──▶ 目标服务器
▲ │
└──────────────────────── 响应 / SSE 事件 ◀──────────────────────────┘- 快应用与手机端通过 AstroBox 的**互联通道(interconnect)**收发 JSON 消息;
- 握手成功后,快应用进入「桥接模式」,把
fetch请求改走互联通道; - SimpleFetch 插件在手机端执行真实 HTTP 请求,并把结果按协议回传。
你不需要关心手机端如何发请求——只要正确收发 SF_* 消息即可。
正在观望?
如果你还没开始开发快应用,ResonaUI 是一个很好的起点:它不仅集成了 SimpleFetch,自带的 AI 提示词也能降低 AI 辅助开发的门槛、提高应用质量。
正式接入
如果你已经有自己的快应用,接入 SimpleFetch 并不难。针对以下两种情况,我们提供了对应的教程:
- 已有通信协议实现:只需为 SimpleFetch 开一个分支处理其消息;
- 还没有互联通信部分:直接复用 ResonaUI 的互联模块即可。
已有通信协议接入
如果你已经有自己的通信协议实现,可以为 SimpleFetch 单独开一个分支来处理它的消息。
获取 simpleFetch.js 源码
任选一种方式:
- 直接从 GitHub 下载:simpleFetch.js
- 通过 git 克隆后提取:
git clone https://github.com/B4QAQ/ResonaUI然后在 src/common/js/simpleFetch.js 找到该文件。
根据实际情况修改依赖
simpleFetch.js 依赖 ResonaUI 的 sendInterconnectData 与 getDeviceInfo,二者都有替换方案。
sendInterconnectData —— 对接你的数据发送函数
/**
* 发送数据到手机 App
* @param {Object} data 要发送的数据对象
* @return {Promise<boolean>} 发送结果
*/
export function sendInterconnectData(data) {
return new Promise((resolve) => {
if (!global.interconnectInstance) {
console.log('[设备通信] 发送失败: 通信实例未初始化')
global.uiAdm.MessageBox('发送数据失败:未初始化')
resolve(false)
return
}
global.interconnectInstance.send({
data: data,
success: () => {
console.log('[设备通信] 发送成功:', JSON.stringify(data))
resolve(true)
},
fail: (data, code) => {
console.log('[设备通信] 发送失败, code:', code, 'msg:', data)
global.uiAdm.MessageBox(`[E${code}]发送数据失败:` + data)
resolve(false)
}
})
})
}将上文的 global.interconnectInstance 替换为你自己通信协议的实例即可。
getDeviceInfo —— 网络状态刷新
/**
* 获取网络信息
* @return {Promise<Object>} 网络信息对象
*/
export async function getDeviceInfo() {
const info = {}
try {
await new Promise((resolve) => {
network.getType({
success: (data) => {
if (global.NetworkStatus === 'bridge') {
info.type = 'bridge'
global.fetchAva = true
} else {
Object.assign(info, data)
}
resolve()
},
fail: () => resolve()
})
})
Object.assign(info, app.getInfo())
if (global.NetworkStatus !== 'bridge') {
global.fetchAva = !!app.canIUse('@system.fetch')
}
global.NetworkStatus = info.type
} catch (e) {
console.error('[X] 获取设备信息失败:', e)
}
return info
}此处对 ResonaUI 的实现做了简写。理论上,getDeviceInfo 只要能正确给 global.NetworkStatus 与 global.fetchAva 赋值即可。
uploadFile —— 文件保存
自SimpleFetch1.2后(JS版本),SimpleFetch集成了文件下载功能,您可以参考以下代码为您的项目集成文件下载功能或直接将其删除
export async function uploadFile(data){
if (!data?.uri) throw new Error('缺少 uri')
if (!data?.data) throw new Error('缺少 data')
const bytes = decodeBase64(data.data)
await ensureDir(data.uri)
await new Promise((resolve, reject) => {
file.writeArrayBuffer({
uri: data.uri,
buffer: bytes,
append: data.append,
position: data.position,
success: resolve,
fail: (d, code) => reject(`写入失败: ${code}`)
})
})
}十分感谢雪松大佬对于文件下载部分的指导
进阶移植:关于`global.uiAdm.MessageBox
代码里出现的 global.uiAdm.MessageBox 只是对 prompt.showToast 的简单封装,具体实现如下,按需替换成你自己的提示组件即可:
/**
* 显示提示框
*/
export function MessageBox(text = '未指定文本', time = 1500) {
prompt.showToast({ message: text, duration: time })
}在实际请求中使用
完成上面两步后,就可以在你的 fetch 封装里使用 SimpleFetch 了:
import fetch from '@system.fetch'
import * as simpleFetch from './simpleFetch.js'
/**
* 发送网络请求
* @param {string} url 请求地址
* @return {Object} 请求结果
*/
export async function sendFetch(url) {
// 检查网络状态
if (global.NetworkStatus === 'none') {
console.log('[X] 无网络连接')
return { status: 503, result: '无网络连接' }
}
const requestConfig = { timeout: 10000 }
requestConfig.url = encodeURI(url)
requestConfig.method = 'GET'
// 桥接模式走 SimpleFetch,否则走原生 fetch
const fetcher = global.NetworkStatus === 'bridge' ? simpleFetch : fetch
try {
const response = await fetcher.fetch(requestConfig)
return response.data.data
} catch (e) {
console.log('[X] 请求异常:', e)
}
}发起下载同理,此处略
设计上,SimpleFetch 的 fetch 函数返回值与 @system.fetch 完全一致,因此可以直接替换系统 fetch 使用。
无通信协议接入
如果你还没有通信协议,接入 SimpleFetch 更加简单——直接复用 ResonaUI 的互联模块即可。
克隆 ResonaUI
git clone https://github.com/B4QAQ/ResonaUI粘贴相关文件
在 ResonaUI 中找到 simpleFetch.js 和 interconnect.js,处理好目录关系(图省事可以把整个目录结构搬过来)。
移植依赖函数
实现网络状态刷新 getDeviceInfo:
/**
* 获取网络信息
* @return {Promise<Object>} 网络信息对象
*/
export async function getDeviceInfo() {
const info = {}
try {
await new Promise((resolve) => {
network.getType({
success: (data) => {
if (global.NetworkStatus === 'bridge') {
info.type = 'bridge'
global.fetchAva = true
} else {
Object.assign(info, data)
}
resolve()
},
fail: () => resolve()
})
})
Object.assign(info, app.getInfo())
if (global.NetworkStatus !== 'bridge') {
global.fetchAva = !!app.canIUse('@system.fetch')
}
global.NetworkStatus = info.type
} catch (e) {
console.error('[X] 获取设备信息失败:', e)
}
return info
}此处对 ResonaUI 的实现做了简写。理论上,getDeviceInfo 只要能正确给 global.NetworkStatus 与 global.fetchAva 赋值即可。
进阶移植:关于global.uiAdm.MessageBox
文件中同样有大量 global.uiAdm.MessageBox,它只是对 prompt.showToast 的简单封装:
/**
* 显示提示框
*/
export function MessageBox(text = '未指定文本', time = 1500) {
prompt.showToast({ message: text, duration: time })
}在 app.ux 中初始化
在 app.ux 中做以下初始化:
import { initInterconnect } from './common/js/interconnect.js'
export default {
async onCreate() {
initInterconnect()
},
}在实际代码中使用
import fetch from '@system.fetch'
import * as simpleFetch from './simpleFetch.js'
/**
* 发送网络请求
* @param {string} url 请求地址
* @return {Object} 请求结果
*/
export async function sendFetch(url) {
// 检查网络状态
if (global.NetworkStatus === 'none') {
console.log('[X] 无网络连接')
return { status: 503, result: '无网络连接' }
}
const requestConfig = { timeout: 10000 }
requestConfig.url = encodeURI(url)
requestConfig.method = 'GET'
// 桥接模式走 SimpleFetch,否则走原生 fetch
const fetcher = global.NetworkStatus === 'bridge' ? simpleFetch : fetch
try {
const response = await fetcher.fetch(requestConfig)
return response.data.data
} catch (e) {
console.log('[X] 请求异常:', e)
}
}发起下载同理,此处略
设计上,SimpleFetch 的 fetch 函数返回值与 @system.fetch 完全一致,因此可以直接替换系统 fetch 使用。
接入后检查清单
-
sendInterconnectData已对接你自己的发送函数(或已复用interconnect.js); -
getDeviceInfo能正确设置global.NetworkStatus与global.fetchAva; -
app.ux启动时调用了initInterconnect()(从零接入时); - 桥接模式下请求确实走
simpleFetch.fetch,非桥接时回退原生@system.fetch; - 握手机制生效:心跳每 10 秒发送,超时能断开并重连。
握手、心跳、分片、SSE 等字段细节与完整时序见 通信协议。