更新记录

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。

技术原理

  1. 通过 DisplayManager 枚举 Display,取第一块非 Display.DEFAULT_DISPLAY 的屏幕
  2. 用该 Display 创建 Presentation(系统级副屏窗口),内部铺满一个 WebView
  3. 主屏 → 副屏:WebView.evaluateJavascript() 调用 window.receiveData(data)
  4. 副屏 → 主屏:addJavascriptInterface 注入 posScreen 对象,@JavascriptInterface 方法回调到 UTS 层
  5. 所有 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

隐私、权限声明

1. 本插件需要申请的系统权限列表:

INTERNET(副屏加载网络页面)、ACCESS_NETWORK_STATE(检测网络状态)

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据。副屏加载的网页由使用者自行提供,插件本身不上传任何信息。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

许可协议

MIT License

Copyright (c) 2026 pos-multiscreen

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

暂无用户评论。