快应用 FeatureFeature API

system.interconnect 设备互联(跨设备通信)

在手表等设备与手机端应用之间建立连接,实现跨设备消息收发、连接状态监听与连接诊断。官方标准文档中本模块名为"设备通信 interconnect":用于和搭配使用的手机 app 进行通信,收发手机 app 数据。

项目说明
模块名system.interconnect
Feature 类型标准 Feature
所需权限无

接口声明

{ "name": "system.interconnect" }

导入方式

// 快应用标准写法
import interconnect from '@system.interconnect'
// 本仓库运行时/测试中使用的写法
const interconnect = require('system.interconnect')

功能概述

system.interconnect 用于设备端(如手表)与手机端指定应用之间的互联通信。底层基于 uv_miwear(libuv 扩展,通过管道与手机端 miwear 服务通信),按包名连接并验证手机上的目标应用,连接建立后可双向收发消息。

官方说明:通信连接会自动建立,应用内不用关心连接的创建和销毁,但是可以注册回调函数来接收连接状态改变的信息,以便于进行相应处理,例如对用户进行提示。

  • require('system.interconnect') 时,Native 会自动以当前快应用的包名发起连接;
  • 通过 interconnect.instance(info) 可以获取互联实例(InterConn),并可用 info.package 指定/覆盖手机端目标应用包名;该实例在快应用线程内是单例,多次调用返回同一个对象(官方说明:获取连接对象,在 app 中以单例形式存在,后续的数据收发都是基于这个连接对象);
  • 实例提供 send(发送数据到手机 App 端)、getReadyState(获取 App 连接状态)、diagnosis(连接诊断)三个方法,以及 onmessage / onopen / onclose / onerror 四个事件属性;
  • 连接断开期间发送的消息会在 timeout 时间内缓存,连接恢复后自动补发,超时则失败。

API

interconnect.instance(info)

获取一个互联实例(InterConn),用于与手机端应用通信。当前底层实现是单例,重复调用返回同一个实例。调用时若连接尚未建立,会自动发起连接。

参数(params 对象字段)

字段类型必填默认值说明
packagestring否无手机端目标应用的包名,用于连接手机上的应用;传入后会覆盖默认包名
fingerprintstring否无应用指纹信息(用于连接验证);当前 Native 实现中仅接收该字段,实际未使用

(参数整体可选:instance(AppInfo info = null),不传时使用当前快应用包名连接。官方文档中 instance 标记为无参数。)

返回值

字段类型说明
返回值object(InterConn 实例)interconnect 的连接实例 connect 对象,拥有下文的方法与事件属性;单例,多次调用返回同一对象

示例

const interconnect = require('system.interconnect')

// 指定手机端目标应用包名,获取互联实例
const conn = interconnect.instance({
  package: 'com.example.phoneapp',
  fingerprint: 'xxxxxxxxxxxxxxxxx'
})

// 也可以不传参数,使用当前快应用包名连接
const conn2 = interconnect.instance

官方示例:

const connect = interconnect.instance

conn.getReadyState(parms)

获取 App 连接状态。异步模型:Promise 风格(promise<object>),同时参数对象中也支持 success / fail 回调(以 JIDL 为准)。

参数(ReadystateParams 对象字段)

字段类型必填默认值说明
successfunction(ReadyStateSuccessCb)否无成功回调,参数为 StatusInfo,见下
failfunction(ReadyStateFailCb)否无失败回调,参数为 (errMsg: string, code: number)

(参数整体可选:getReadyState(ReadystateParams parms = null)。)

成功返回 / success 回调参数

StatusInfo 对象:

字段类型说明
statusnumber(int)连接状态:1 表示连接成功(手机端应用连接建立);2 表示连接断开/未连接(连接断开、连接失败、手机端应用未安装、连接已关闭等;当前 Native 实现中连接建立过程中也返回 2)

Native 源码注释中曾约定 0 表示连接中,但当前实现对未连接状态统一返回 2,具体以 Native 实现为准。

失败回调 fail / Promise reject

  • 参数:(errMsg: string, code: number)
  • 错误码/错误信息:
codemessage触发场景
1000conn has been shoutdown互联实例已销毁(Feature 销毁后调用)

官方错误码说明:支持通用错误码,1006 表示连接断开。

示例

const interconnect = require('system.interconnect')
const conn = interconnect.instance

// 回调风格
conn.getReadyState({
  success: function (ret) {
    if (ret.status === 1) {
      console.log('连接成功')
    } else if (ret.status === 2) {
      console.log('连接失败')
    }
  },
  fail: function (errMsg, code) {
    console.log('getReadyState fail', code, errMsg)
  }
})

// Promise 风格
conn.getReadyState.then(function (ret) {
  console.log('status =', ret.status)
}).catch(function (err) {
  console.log('fail', err)
})

conn.send(parms)

发送数据到手机 App 端。异步模型:Promise 风格(promise<void>)。

  • 已连接时立即发送;
  • 连接正在建立时,消息会缓存为 pending 任务,连接建立后自动发送;在 timeout 时间内连接仍未建立则超时失败;
  • 已断开连接时直接失败。

参数(SendParams 对象字段)

字段类型必填默认值说明
dataobject是null发送的数据,必须为对象;Native 会将其序列化为字符串(JSON)后发送,序列化结果不能为空字符串
timeoutnumber(int)否1000连接未建立时消息缓存的最长等待时间,单位毫秒;超时后本次发送失败

成功返回 / success 回调参数

无(promise<void>,resolve 不带数据)。

失败回调 fail / Promise reject

  • 参数:(errMsg: string, code: number)
  • 错误码/错误信息:
codemessage触发场景
202invalid argsdata 为空、不是对象、无法序列化为字符串或序列化为空字符串
204send timeout连接建立过程中缓存的发送任务等待超时(timeout 毫秒内未连上;官方错误码说明:204 链接超时)
1006disconnect当前处于断开状态,无法发送(官方错误码说明:1006 连接断开)
1006connection broken消息已发出但底层发送失败(连接中断)
1006conn has been shoutdown互联实例已销毁

示例

const interconnect = require('system.interconnect')
const conn = interconnect.instance

// Promise 风格
conn.send({
  data: { hello: 'world' },
  timeout: 2000
}).then(function  {
  console.log('发送成功')
}).catch(function (err) {
  console.log('发送失败', err)
})

// 使用默认 timeout(1000ms)
conn.send({ data: { msg: 'hello phone' } })

官方示例(回调风格):

connect.send({
  data: {
    str: 'test',
    num: 123
  },
  success:  => {
    console.log(`handling success`)
  },
  fail: (data, code) => {
    console.log(`handling fail, errMsg = ${data.data}, errCode = ${data.code}`)
  }
})

conn.diagnosis(param)

诊断手表应用和对端应用的连接情况:如果连接成功则返回 ok,连接失败则返回失败原因。连接正在建立时,会等待连接结果(最多等待 timeout 毫秒)后再返回最终状态;连接已有明确结果(已连接/已断开/应用未安装等)时立即返回。同一时间只允许一个诊断任务。异步模型:Promise 风格(promise<object>)。

参数(DiagnosisParams 对象字段)

字段类型必填默认值说明
timeoutnumber(int)否10000等待诊断/连接结果的超时时间,单位毫秒(官方默认值:10000ms);必须为正数,同一时间只允许一个诊断任务

(参数整体可选:diagnosis(DiagnosisParams param = null),不传时使用默认超时 10000ms。)

成功返回 / resolve 值

resolve 一个对象,status 字段表示诊断结果(注意:诊断完成本身走 resolve,具体连接结果看 status):

字段类型说明
statusnumber(int)0:OK 链接成功;204:CONNECT_TIMEOUT 连接超时;1001:APP_UNINSTALLED 对端应用未安装;1000:OTHERS 其他链接错误(连接失败/连接断开)

失败回调 fail / Promise reject

  • 参数:(errMsg: string, code: number)
  • 错误码/错误信息:
codemessage触发场景
1000timeout should be positivetimeout 小于等于 0
1000diag上一个诊断任务尚未结束(同一时间只允许一个 pending 诊断)
1006conn has been shoutdown互联实例已销毁

示例

const interconnect = require('system.interconnect')
const conn = interconnect.instance

conn.diagnosis({ timeout: 5000 }).then(function (ret) {
  switch (ret.status) {
    case 0:
      console.log('连接正常')
      break
    case 204:
      console.log('诊断超时')
      break
    case 1001:
      console.log('手机端应用未安装')
      break
    default:
      console.log('连接异常,status =', ret.status)
  }
}).catch(function (err) {
  console.log('diagnosis fail', err)
})

Promise 返回形式(社区资料:洛汐文档库——同源 JIDL 把 getReadyState/diagnosis/send 定义为 Promise;不同框架版本可能同时支持 callback 与 Promise 两种风格,应以返回对象是否具有 .then 为准):

const state = await connect.getReadyState
const diagnosis = await connect.diagnosis({ timeout: 10000 })
await connect.send({ data: payload, timeout: 1000 })

conn.getApkStatus

已废弃:JIDL 注释标明"废弃的接口,保持兼容性",新代码请使用 getReadyState / diagnosis / onopen / onclose(社区资料:洛汐文档库同样标注该方法在 JIDL 中明确标记为废弃,新代码应使用 getReadyState 和 diagnosis,因为后者能区分连接超时、应用未安装和其他错误)。

同步获取手机端应用的连接状态字符串。

参数

无。

返回值

字段类型说明
返回值stringCONNECTED(手机端应用已连接)、DISCONNECTED(通道存在但当前未连接/已断开/其他状态/实例已销毁)、UNINSTALLED(手机端应用未安装)三者之一

示例

const interconnect = require('system.interconnect')
const conn = interconnect.instance

console.log(conn.getApkStatus) // 'CONNECTED' | 'DISCONNECTED' | 'UNINSTALLED'

// 社区资料:调用前可做能力判断
if (typeof conn.getApkStatus === 'function') {
  console.log(conn.getApkStatus)
}

属性(事件)

属性类型读写说明
onmessagefunction(OnMessage)可读写接收手机 App 端数据时触发,回调参数为 Message 对象({ data: string }),data 为消息内容字符串
onopenfunction(OnOpen)可读写连接打开时触发(首次连接与断线重连都会触发),回调参数为 object,Native 实现中该对象包含布尔字段 isReconnected(是否是重新连接)
onclosefunction(OnClose)可读写连接关闭时触发,回调参数为 CloseInfo 对象({ data: string, code: number }),data 为原因描述
onerrorfunction(OnError)可读写连接出错时触发,回调参数为 ErrorInfo 对象({ data: string, code: number })

赋值新回调会替换并移除旧回调。

onclose / onerror 事件的 code 与 data(Native 实证)

事件codedata触发场景
onerror1000connect to app failed连接手机端应用失败(未知错误)
onerror1006connect to miwear server failed连接建立过程中连接断开(连接失败)
onclose1000disconnect已连接后连接断开
onclose1001phone app uninstalled手机端应用被卸载

官方标准文档描述:官方文档将错误码 1000(未知错误)、1001(手机 APP 未安装)、1006(连接断开)均列在 onerror 的错误码说明中。

示例

const interconnect = require('system.interconnect')
const conn = interconnect.instance

conn.onmessage = function (msg) {
  console.log('received message:', msg.data)
}

conn.onopen = function (info) {
  // info 为对象,Native 实现中包含 isReconnected 布尔字段
  console.log('connection opened isReconnected: ', info.isReconnected)
}

conn.onclose = function (info) {
  console.log('connection closed, reason =', info.data, 'code =', info.code)
}

conn.onerror = function (err) {
  console.log('connection error, errMsg =', err.data, 'errCode =', err.code)
}

数据结构

AppInfo

instance(info) 的参数,描述手机端目标应用信息。

字段类型说明
packagestring手机端应用包名,用于连接并验证手机上的应用
fingerprintstring应用指纹(当前 Native 实现中未实际使用)

StatusInfo

getReadyState 成功回调 / resolve 值。

字段类型说明
statusint连接状态:1 连接成功;2 连接断开

Message

onmessage 回调参数。

字段类型说明
datastring接收的数据(消息内容字符串)

CloseInfo

onclose 回调参数。

字段类型说明
datastring连接关闭返回的数据(关闭原因 reason)
codeint链接关闭状态码

ErrorInfo

onerror 回调参数。

字段类型说明
datastring错误信息
codeint错误码

FailInfo

send 失败回调结构(JIDL 中定义,Promise reject 时对应 (data, code))。

字段类型说明
datastring错误描述
codeint错误码

SendParams

send 的参数。

字段类型说明
dataobject待发送数据,默认 null;Native 序列化为字符串发送,不能为空
timeoutint发送缓存等待超时(毫秒),默认 1000

DiagnosisParams

diagnosis 的参数。

字段类型说明
timeoutint诊断等待超时(毫秒),默认 10000

ReadystateParams

getReadyState 的参数。

字段类型说明
successcallback(ReadyStateSuccessCb)成功回调,参数为 StatusInfo
failcallback(ReadyStateFailCb)失败回调,参数为 (errMsg: string, code: int)

错误码汇总

Native 实现(StatusCode)中定义的错误码:

code枚举名含义
0kOk成功(diagnosis 的 status)
202kInvalidArgs参数错误(send 的 data 非法;diagnosis 内部校验超时参数时使用,reject 给 JS 时为 1000)
203kUnsupported不支持(已有 pending 诊断任务时内部返回,reject 给 JS 时为 1000)
204kTimeout超时(send 超时失败;diagnosis 的 status)
1000kUnknown未知错误(连接失败/断开;onerror/onclose 事件码;diagnosis 的 status 与 reject 码;实例销毁后 getReadyState reject)
1001kAppUninstall手机端应用未安装(diagnosis 的 status;onclose 事件码)
1006kDisconnect连接断开(send reject;实例销毁后 send/diagnosis reject;onerror 事件码)

注意事项

  • 自动连接:require('system.interconnect') 时 Native 即以当前快应用包名自动发起连接(底层 uv_miwear_connect,基于管道连接手机端 miwear 服务);instance 返回单例,多次调用得到同一个 InterConn 对象。应用无需关心连接的创建和销毁,注册事件回调即可接收连接状态变化(官方说明)。
  • 消息缓存:连接建立过程中调用 send,消息会缓存并在连接成功后自动发送(ProcessPendingSendTasks);超过 timeout 未连上则以 204 超时失败。连接已断开时 send 立即以 1006 失败。
  • 数据格式:send 的 data 必须是对象,Native 通过字符串转换(JSON 序列化)后发送;空字符串/非对象都会返回 202 invalid args。
  • 诊断串行:同一时间只允许一个 pending 的 diagnosis 调用,重复调用会 reject(diag);timeout 必须为正数。
  • 事件补发:onerror 未注册时发生的错误会被缓存(仅保留最新一条),之后注册 onerror 时立即补发;连接已建立后再注册 onopen,也会补发一次连接事件。
  • onopen 参数:JIDL 声明回调参数为 object is_reconnect,Native 实际传入对象的字段名为 isReconnected(boolean);源码注释中首次连接/重连的布尔取值描述存在不一致,具体行为以 Native 实现为准。
  • 生命周期:Feature 销毁时会关闭 miwear 连接、停止定时器并清理 pending 的发送/诊断任务;此时尚未 settle 的 Promise 不会再回调(Native 打印 unrejected ... 警告日志),实例销毁后调用各方法会以 1006/1000 失败。
  • getApkStatus 为废弃接口,仅为兼容性保留;新代码使用 getReadyState 与 diagnosis(后者可区分连接超时、应用未安装和其他错误,社区资料:洛汐文档库)。

官方开发注意事项:包名与签名

interconnect 通信前提要保证快应用和三方应用安卓端两者的包名及签名保持一致(官方标准文档):

  • 保证快应用 manifest.json 里 package 字段与需要接入的三方 app 安卓端包名一致。
  • 快应用签名需要使用三方应用安卓端签名,可以从 .jks 中提取证书及私钥,方法如下:
  1. 先将 jks 转换成 p12,执行以下命令,输入相应密码后,在同级目录下生成对应的 p12 格式文件:
keytool -importkeystore -srckeystore keystore.jks -destkeystore keystore.p12 -srcstoretype jks -deststoretype pkcs12
  1. 再将 p12 转 pem,执行以下命令,输入上一步设置的 p12 文件密码后,在同级目录下生成对应的 pem 格式文件:
openssl pkcs12 -nodes -in keystore.p12 -out keystore.pem
  1. 从 pem 格式文件中复制出私钥和证书:把 -----BEGIN PRIVATE KEY----- 到 -----END PRIVATE KEY----- 的内容复制到 private.pem 中;把 -----BEGIN CERTIFICATE----- 到 -----END CERTIFICATE----- 的内容复制到 certificate.pem 中。
  • 如果本地没有安装 OpenSSL 或想要更简便的操作流程,官方提供了在线签名生成工具。该工具是一个基于 WebAssembly 编写的 Web 应用,可以在浏览器环境中直接生成 pem 格式的私钥和证书,无需将签名文件和密码上传到远程服务器。步骤:上传 p12 文件并输入对应的密码;点击"生成签名"按钮,等待签名生成成功弹窗;点击"下载签名"按钮,下载 pem 格式的私钥和证书。
  • 快应用需要将生成的私钥 private.pem 和证书 certificate.pem 放在快应用根目录 /sign/debug 和 /sign/release 下出包测试。
  • 真机测试时建议先输入包名 uninstall 老包再安装新包,可以观察桌面图标卸载(卸载会删除应用图标)保证彻底替换。

官方参考附录:小米穿戴第三方 APP 能力开放接口文档、interconnect 开发测试 demo(见官方文档页下载链接)。

机型支持明细

设备产品说明
小米 S1 Pro 运动健康手表支持
小米手环 8 Pro支持
小米手环 9 / 9 Pro支持
Xiaomi Watch S3支持
Redmi Watch 4支持
小米腕部心电血压记录仪支持
小米手环 10支持
小米手环 10 Pro支持
Xiaomi Watch S4支持
REDMI Watch 5支持
REDMI Watch 6支持
Xiaomi Watch S5支持

源码

openvela 快应用代码仓库:openvela/frameworks/runtimes/feature/(JIDL 接口与 Native 实现)、openvela/packages/(使用示例);引擎内置 Feature 预编译于 libquickappfeatures。

参考来源

大纲