更新记录
1.0.0(2026-10-23) 下载此版本
首个发布版本。
- 双屏异显:基于 Android
Presentation+WebView,在第二块屏幕独立渲染网页 - 双向通讯:主屏
sendDataToSecondScreen推送,副屏window.receiveData接收;副屏posScreen.sendToFirstScreen回传 - 设备检测:
isSecondScreenAvailable/getSecondScreenInfo获取副屏可用性与分辨率 - 事件监听:
onReceiveData/onPageFinished/onPageError - 支持远程 URL 与本地
hybrid/html页面 - 默认放行 SSL 自签名证书,适配收银机内网环境
- 重复打开自动释放旧副屏实例,避免多开叠加
- 副屏桥接对象统一为
posScreen,保留lyjsdk别名以兼容存量页面 - 附带示例工程(主屏控制台 + 副屏演示页)
平台兼容性
uni-app(4.0)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-vue插件版本 | app-nvue | app-nvue插件版本 | Android | Android插件版本 | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | √ | × | × | √ | 1.0.0 | √ | 1.0.0 | 8.0 | 1.0.0 | × | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
uni-app x(4.0)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | - | - | - | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 | 蒸汽模式 |
|---|---|---|---|
| × | × | √ | √ |
POS-双屏异显异触(pos-multiscreen)
Android 双屏异显异触 UTS 插件。基于 Android Presentation + WebView,在主屏之外的第二块屏幕上独立显示网页内容,并支持 主屏 ↔ 副屏双向实时通讯。适用于安卓收银机、POS 机、自助点餐机、双屏广告机等设备的「客显屏」场景。
纯 UTS + Kotlin 混编实现,无第三方 SDK、无 jar 依赖,云打包即可使用。
功能特性
| 能力 | 说明 |
|---|---|
| 副屏异显 | 在第二块屏幕独立展示 WebView 页面,主屏内容不受影响 |
| 页面来源 | 支持 http/https 远程地址,也支持本地 hybrid/html 页面 |
| 双向通讯 | 主屏 sendDataToSecondScreen 推数据,副屏 window.receiveData 接收;副屏 posScreen.sendToFirstScreen 回传 |
| 设备检测 | isSecondScreenAvailable 判断设备是否有副屏,getSecondScreenInfo 读取每块屏幕的分辨率 / displayId |
| 加载事件 | onPageFinished / onPageError 监听副屏页面加载结果,便于做失败重试 |
| 自签名证书 | 默认放行 SSL 错误,适配收银机内网 HTTPS 环境 |
| 多屏枚举 | 返回设备全部显示器信息,可自选目标屏幕 |
| 轻量稳定 | 仅依赖系统 Presentation + WebView,无多余依赖,重复打开自动释放旧实例 |
平台支持
| 平台 | 支持情况 |
|---|---|
Android App(app-android) |
✅ 支持(minSdkVersion 21 / Android 5.0+) |
iOS App(app-ios) |
❌ 不支持 |
| H5 / 小程序 / 快应用 | ❌ 不支持(相关逻辑需用条件编译隔离) |
| uni-app(Vue2 / Vue3) | ✅ 支持 |
| HBuilderX | 4.0.0 及以上 |
需要真实双屏硬件才能看到效果。单屏手机 / 平板调用
openSecondScreen会返回9010002,可用isSecondScreenAvailable提前判断。
安装
方式一:插件市场安装(推荐)
在 HBuilderX 中打开项目 → 插件市场搜索 pos-multiscreen → 点击「下载插件并导入 HBuilderX」。
方式二:手动放入
将 uni_modules/pos-multiscreen 整个目录复制到你的项目 uni_modules 目录下,重新运行即可(UTS 插件修改后需重新打自定义基座或云打包)。
安装后目录结构:
uni_modules/
└── pos-multiscreen/
├── package.json
├── readme.md
└── utssdk/
├── interface.uts # 对外类型声明
└── app-android/
├── config.json # minSdkVersion / abi
├── index.uts # 插件 API 实现
└── hybrid.kt # Presentation + WebView 混编实现
快速开始
1. 引入插件
import {
openSecondScreen,
closeSecondScreen,
sendDataToSecondScreen,
isSecondScreenAvailable,
getSecondScreenInfo,
onReceiveData,
onPageFinished,
onPageError
} from '@/uni_modules/pos-multiscreen'
2. 准备副屏页面
把副屏页面放到项目的 hybrid/html/ 目录,例如 hybrid/html/second-screen.html:
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>副屏</title></head>
<body>
<h1 id="tip">等待主屏推送…</h1>
<script>
// 接收主屏数据(插件会调用这个函数)
window.receiveData = function (data) {
document.getElementById('tip').innerText = data
}
// 向主屏回传数据(posScreen 为桥接对象)
function callStaff() {
window.posScreen.sendToFirstScreen(JSON.stringify({ action: 'callStaff' }))
}
</script>
</body>
</html>
3. 注册监听、打开副屏
onReceiveData({
callback: (data) => {
console.log('收到副屏消息:', data)
}
})
onPageFinished({
callback: (url) => {
console.log('副屏页面加载完成:', url)
}
})
onPageError({
callback: (code, desc) => {
console.error('副屏加载失败:', code, desc)
}
})
// 打开本地页面(App 端)
openSecondScreen({
url: plus.io.convertLocalFileSystemURL('/hybrid/html/second-screen.html'),
success: () => console.log('副屏已打开'),
fail: (err) => console.error(err.errCode, err.errMsg)
})
4. 主屏推数据、关闭副屏
sendDataToSecondScreen({
data: JSON.stringify({ type: 1, no: 'A001', total: 42.0 }),
success: () => console.log('已推送'),
fail: () => console.log('副屏未打开')
})
closeSecondScreen({
success: () => console.log('副屏已关闭')
})
API 参考
openSecondScreen(options)
打开副屏(已打开时会先关闭旧实例再加载新地址)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
url |
String |
是 | 副屏页面地址。远程用 http(s)://,本地用 plus.io.convertLocalFileSystemURL() 转换后的绝对路径 |
success |
Function |
否 | 打开成功回调,res = { errCode: 0, errMsg } |
fail |
Function |
否 | 打开失败回调,res = { errCode, errMsg } |
complete |
Function |
否 | 完成回调(成功失败都会执行) |
closeSecondScreen(options)
关闭副屏并销毁内部 WebView。重复调用不会报错。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
success / fail / complete |
Function |
否 | 回调,res = { errCode, errMsg } |
sendDataToSecondScreen(options)
主屏向副屏推送数据。插件通过 evaluateJavascript 调用副屏的 window.receiveData(data)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
data |
String |
是 | 要发送的内容。结构化数据请自行 JSON.stringify |
success / fail / complete |
Function |
否 | 副屏未打开时走 fail(errCode: 9010003) |
注意:
receiveData需要在副屏页面onPageFinished之后才存在。若在打开副屏的瞬间立即推送,建议在onPageFinished回调里发首屏数据。
isSecondScreenAvailable(options)
检查设备是否存在副屏。
isSecondScreenAvailable({
success: (res) => {
// res.available Boolean 是否有副屏
// res.displayCount Number 显示器数量
}
})
getSecondScreenInfo(options)
获取显示器列表与副屏状态。
getSecondScreenInfo({
success: (res) => {
// res.isOpen Boolean 副屏是否已打开
// res.currentUrl String 副屏当前地址
// res.info String JSON 字符串,结构见下
const info = JSON.parse(res.info)
// info.displays = [{ displayId, name, isDefault, width, height }, ...]
}
})
事件监听(持续回调)
| 方法 | 回调签名 | 说明 |
|---|---|---|
onReceiveData({ callback }) |
(data: String) => void |
收到副屏通过桥接回传的数据 |
onPageFinished({ callback }) |
(url: String) => void |
副屏页面加载完成 |
onPageError({ callback }) |
(errorCode: Number, description: String) => void |
副屏页面加载失败 |
同类监听重复注册会覆盖上一个回调,只需在
onLoad注册一次,插件内部保持引用持续有效。
错误码
| errCode | 含义 | 常见原因 |
|---|---|---|
9010001 |
url 为空 | 未传 url 或传了空字符串 |
9010002 |
打开副屏失败 | 设备只有一块屏幕,或系统未暴露第二块 Display |
9010003 |
副屏未打开,无法发送 | 未调用 openSecondScreen 或已关闭 |
9010004 |
获取屏幕信息失败 | 极少出现,通常是 DisplayManager 异常 |
副屏页面桥接 API
插件在副屏 WebView 中注入 window.posScreen 对象(同时保留别名 window.lyjsdk,方便从旧插件迁移)。
| 方法 | 说明 |
|---|---|
posScreen.sendToFirstScreen(data) |
副屏 → 主屏发送字符串。建议传 JSON 字符串,主屏用 onReceiveData 接收后 JSON.parse |
posScreen.getScreenInfo() |
返回副屏信息 JSON 字符串:{ width, height, displayId } |
window.receiveData(data) |
需由副屏页面自行定义,主屏推送数据时被调用 |
示例:
// 副屏页面
window.receiveData = function (data) {
const msg = JSON.parse(data)
if (msg.type === 1) {
renderOrder(msg)
}
}
// 副屏上报
window.posScreen.sendToFirstScreen(JSON.stringify({
action: 'invoice',
at: Date.now()
}))
页面加载过程中
posScreen尚不存在,调用前请先判空:window.posScreen && window.posScreen.sendToFirstScreen(...)。
通讯协议建议
插件只负责「传字符串」,协议由业务自定义。推荐的轻量约定(示例工程即采用此结构):
// 主屏 → 副屏
{ type: 0 } // 待机
{ type: 1, queue: 'A123', no: 'NO20261023001', // 订单明细
shop: '示例商户', time: '12:03',
items: [{ name: '宫保鸡丁饭', qty: 1, price: 22.0 }],
discount: 4.0, total: 80.0 }
{ type: 2, method: '微信支付', amount: 80.0 } // 支付成功
{ type: 3, shop: '示例商户', text: '欢迎光临' } // 欢迎语
// 副屏 → 主屏
{ action: 'callStaff', at: 1730000000000 } // 呼叫服务员
{ action: 'invoice', at: 1730000000000 } // 请求开票
示例副屏页对订单消息做了容错,字段都可缺省:
| 字段 | 类型 | 说明 |
|---|---|---|
queue |
String | 取餐号,显示在左侧大卡片。缺省时回退用 no |
no |
String | 订单号,显示在右上角 |
shop |
String | 门店名,显示在左上角 |
time |
String | 下单时间,缺省取当前时间 |
items |
Array | { name, qty, price },列表超出可视高度自动滚动 |
discount |
Number | 优惠金额,大于 0 时在合计左侧显示 |
total |
Number | 合计金额,缺省按 items 自动累加 |
常见问题
Q:打开副屏没反应,回调 9010002?
设备只有一块屏幕,或系统未把第二块屏作为独立 Display 暴露。先用 isSecondScreenAvailable 看 displayCount,多数安卓收银机需在系统设置里把副屏模式设为「异显 / 扩展屏」而不是「同显 / 镜像」。
Q:本地 HTML 打不开?
必须用 plus.io.convertLocalFileSystemURL('/hybrid/html/xxx.html') 转成真实路径再传入。直接传 /hybrid/html/xxx.html 会被拼成 file:///hybrid/...,路径不存在。
Q:副屏白屏 / 收不到数据?
先看 onPageFinished 是否触发。页面没加载完就 sendDataToSecondScreen,window.receiveData 还不存在,数据会被丢弃。建议在 onPageFinished 里推首屏数据。
Q:HTTPS 自签名证书报错?
插件默认 onReceivedSslError 直接 proceed(),已放行。若仍失败,多半是网络不可达而非证书问题。
Q:可以同时开多个副屏吗?
不能。插件是设备级单例,只维护一块副屏(取第一块非默认 Display)。多屏设备如需指定屏幕,可用 getSecondScreenInfo 拿到全部 displayId 后按需扩展。
Q:打包报 minSdkVersion 冲突?
插件要求 minSdkVersion >= 21,请在 manifest.json → App 原生层配置里确认最低 SDK 不低于 21。
Q:上传云打包后提示插件未购买?
本插件免费,插件市场「普通授权版 / 源码授权版」均为 0 元,勾选授权即可,无需付费。
权限说明
| 权限 | 用途 |
|---|---|
android.permission.INTERNET |
副屏 WebView 加载网络页面 |
android.permission.ACCESS_NETWORK_STATE |
检测网络状态 |
插件不采集任何用户数据,不上报任何信息,无广告、无第三方 SDK。
技术原理
- 通过
DisplayManager枚举Display,取第一块非Display.DEFAULT_DISPLAY的屏幕 - 用该
Display创建Presentation(系统级副屏窗口),内部铺满一个WebView - 主屏 → 副屏:
WebView.evaluateJavascript()调用window.receiveData(data) - 副屏 → 主屏:
addJavascriptInterface注入posScreen对象,@JavascriptInterface方法回调到 UTS 层 - 所有 Presentation 操作通过
Handler(Looper.getMainLooper())切到主线程执行,避免线程异常
示例工程
本插件附带可直接运行的示例工程(插件市场下载包内):
├── uni_modules/pos-multiscreen/ # 插件本体
├── pages/index/index.vue # 主屏控制台:状态检测 / 打开 / 推送 / 日志
├── hybrid/html/second-screen.html # 副屏演示页:待机 / 订单 / 支付成功 三态
├── manifest.json
└── pages.json
运行步骤:HBuilderX 打开示例工程 → manifest.json 里重新获取一个 AppID → 用自定义基座或云打包运行到双屏安卓设备(UTS 插件不支持标准基座直接运行)。
更新日志
1.0.0
- 首个版本,基于生产项目
wb-multiscreen重构并更名发布 - 支持副屏异显、主副屏双向通讯、多屏枚举、页面加载事件
- 副屏桥接对象新增
posScreen,保留lyjsdk别名向下兼容 - 附带完整示例工程与副屏演示页
关于
插件由实际商用的收银系统(安卓双屏 POS)项目中沉淀而来,已在门店日常收银场景稳定运行。问题反馈请通过插件市场留言,或邮件联系作者。
License
MIT

收藏人数:
下载插件并导入HBuilder
下载示例项目ZIP
赞赏(0)
下载 7
赞赏 0
下载 12707000
赞赏 1957
赞赏
京公网安备:11010802035340号