IEvccApiCallback 回调接口¶
最后更新:
AIDL:
com.kooo.evcc.evccapi.IEvccApiCallback
| 契约状态 | 值 |
|---|---|
| 引入版本 | EvccApi 1.0 |
| 最近接口变更 | EvccApi 1.1 |
| 稳定性 | 稳定公开接口 |
| 弃用状态 | 未弃用 |
IEvccApiCallback 是客户端在 openSession() 时交给 EVCC 的单向事件通道。属性和变量监听、来源状态、
Shell 最终结果、会话关闭及重同步通知都从这里返回。
oneway interface IEvccApiCallback {
void onSessionClosed(in EvccOperationResult reason);
void onPropertyEvent(
in EvccPropertyRecord record,
long sequence,
String eventType
);
void onVariableEvent(
in EvccVariableRecord record,
long sequence,
String eventType
);
void onSourceStateChanged(in EvccSourceState state, long sequence);
void onShellCompleted(in EvccShellResult result);
void onResyncRequired(long expectedSequence, long actualSequence);
}
回调通用规则¶
oneway 不等于主线程¶
所有方法都是 oneway Binder 回调:EVCC 提交回调后不会等待客户端业务处理完成。Raw AIDL 客户端收到
回调时通常位于 Binder 线程,必须快速复制/入队;数据库、文件、网络和复杂解析要转交自己的执行器。
EvccApiClient 会把 Listener 回调分发到主线程,但应用仍应把耗时工作移出主线程。
回调可以与同步会话方法并发。例如 replaceSubscriptions() 产生的 INITIAL_SNAPSHOT 可能在调用方处理
同步返回前进入客户端队列。调用前先准备好接收状态,不要依赖“方法返回后才会回调”。
共享事件序号¶
属性事件、变量事件和来源状态共用一个会话级 sequence:
- 新会话从 1 开始递增;
- 三类事件必须按同一条序列检查,不能各自维护计数器;
- Shell 完成和会话关闭不参与该序列;
- 发现
sequence != lastSequence + 1时,已有增量基线可能不完整,应重新提交属性和变量订阅; - 新建会话后序号重新开始,不能跨会话比较。
服务端普通事件队列当前最多暂存 128 条。队列溢出时会清除已失去连续性的积压事件,并发送一次
onResyncRequired();该容量是保护边界,不是允许客户端长期阻塞回调的承诺。
建议的 Raw AIDL 处理骨架¶
private val callback = object : IEvccApiCallback.Stub() {
override fun onPropertyEvent(
record: EvccPropertyRecord,
sequence: Long,
eventType: String,
) {
callbackExecutor.execute { consumeProperty(record, sequence, eventType) }
}
override fun onVariableEvent(
record: EvccVariableRecord,
sequence: Long,
eventType: String,
) {
callbackExecutor.execute { consumeVariable(record, sequence, eventType) }
}
// 其余方法同样只做快速转交。
}
客户端必须在会话存活期间保持 callback 强引用。callback Binder 死亡时 EVCC 会关闭会话并清理需求。
onSessionClosed(reason)¶
用途:通知客户端当前会话已经终止,不能再提交业务调用。
参数格式:
| 参数 | 类型 | 含义 |
|---|---|---|
reason |
EvccOperationResult |
status 是关闭类别,message 是展示/诊断说明 |
常见状态:
PERMISSION_DENIED:用户撤权、授权修订号/UID/签名变化,或回调前实时校验失败;SERVICE_DISABLED:EVCC 开放 API 被关闭;SESSION_CLOSED:服务停止、全部解绑、客户端进程退出或回调处理持续过慢。
客户端动作:停止使用旧 IEvccApiSession,取消依赖该会话的等待任务并更新 UI。只有临时服务断连
适合退避重绑;明确撤权或服务关闭时等待用户处理后再主动连接。
override fun onSessionClosed(reason: EvccOperationResult) {
currentSession = null
notifyUi(reason.status, reason.message)
}
注意事项:这是终态回调,没有 sequence。EVCC 在它之前会清空未发送的普通和 Shell 回调;收到后
不要等待该会话的其他结果。客户端主动调用 close() 时不会再收到本回调。
onPropertyEvent(record, sequence, eventType)¶
用途:发送已订阅属性的初始值和后续变化。
参数格式:
| 参数 | 类型 | 含义 |
|---|---|---|
record |
EvccPropertyRecord |
已解析来源、描述符、类型化值和当前状态 |
sequence |
long |
会话级共享事件序号 |
eventType |
String |
本次事件的语义类型 |
事件类型:
eventType |
含义 | 客户端处理 |
|---|---|---|
INITIAL_SNAPSHOT |
新订阅或来源换代后的基线 | 替换该物理目标的旧值,即使状态是 PENDING |
VALUE_CHANGED |
同一来源、ID、区域的值变化 | 在连续序号基线上应用增量 |
SOURCE_CHANGED |
AUTO 目标切换到另一实际来源 | 同时更新来源身份和当前值,不只替换 value |
REMOVED 是共享事件常量,但当前 1.0 仅用于变量删除;属性订阅路径不会发送该类型。
状态处理:事件到达不等于值可用。始终先检查 record.status,再检查
record.value?.initialized == true;初始属性尚无真实值时可返回 PENDING。
override fun onPropertyEvent(
record: EvccPropertyRecord,
sequence: Long,
eventType: String,
) {
checkSequenceOrResync(sequence)
val key = Triple(
record.resolvedSource,
record.descriptor?.canonicalId,
record.descriptor?.areaId,
)
propertyState[key] = record
}
注意事项:AUTO 订阅在正常瞬时断连时保持原来源,不立即切换;来源关闭或持续不支持后才可能产生
SOURCE_CHANGED。一次回调可能因 Binder 大小预算省略超大值并返回 PAYLOAD_TOO_LARGE。
onVariableEvent(record, sequence, eventType)¶
用途:发送已订阅变量的初始状态和后续变化。
参数格式:
| 参数 | 类型 | 含义 |
|---|---|---|
record |
EvccVariableRecord |
变量名、类型化值、状态、更新时间及可变性 |
sequence |
long |
与属性和来源事件共享的会话级序号 |
eventType |
String |
INITIAL_SNAPSHOT、VALUE_CHANGED 或 REMOVED |
行为:订阅合法但当前不存在的变量也会产生 INITIAL_SNAPSHOT,此时记录状态为 NOT_FOUND。
REMOVED 表示变量被删除;不能把 value == null 单独当作传输错误。
override fun onVariableEvent(
record: EvccVariableRecord,
sequence: Long,
eventType: String,
) {
checkSequenceOrResync(sequence)
if (eventType == EvccApiContract.EventType.REMOVED) {
variables.remove(record.name)
} else {
variables[record.name] = record
}
}
注意事项:重新调用 replaceVariableSubscriptions() 后,旧订阅代次中已排队但尚未发送的事件会被
服务丢弃,因此客户端可能观察到序号缺口。将缺口视为重同步信号,不要自行猜测缺失变量值。
onSourceStateChanged(state, sequence)¶
用途:通知与当前属性订阅相关的数据源状态或连接代次变化。
参数格式:
| 参数 | 类型 | 含义 |
|---|---|---|
state |
EvccSourceState |
来源 ID、AVAILABLE/PENDING/UNAVAILABLE、说明和来源代次 |
sequence |
long |
与属性和变量事件共享的会话级序号 |
override fun onSourceStateChanged(state: EvccSourceState, sequence: Long) {
checkSequenceOrResync(sequence)
sourceStates[state.source] = state
}
注意事项:sourceGeneration 只在同一来源内比较变化,不能跨来源比较大小。状态回调用于解释订阅
为何等待或切换,不要求第三方应用自行连接、断开或维护五个上游来源。
onShellCompleted(result)¶
用途:返回 executeShell() 的最终执行结果。
参数格式:EvccShellResult:
| 字段 | 含义 |
|---|---|
requestId |
与提交/取消使用的 ID 一致 |
status |
最终 OK、FAILED、CANCELLED 或 PERMISSION_DENIED 等 |
exitCode |
进程退出码;未启动或无退出码时为 -1 |
stdout / stderr |
受权限和大小预算限制的输出 |
truncated |
输出是否被裁剪 |
durationMs |
从开始执行到结束的耗时 |
override fun onShellCompleted(result: EvccShellResult) {
pendingShell.remove(result.requestId)
if (result.status == EvccApiContract.Status.OK && result.exitCode == 0) {
showOutput(result.stdout, result.truncated)
} else {
showFailure(result.status, result.stderr)
}
}
注意事项:本回调没有事件序号,必须用 requestId 关联。Shell 结果使用独立的高优先级队列,当前
最多暂存 16 条;队列满时 EVCC 会关闭处理过慢的会话,而不会静默丢失不可重放的结果。即使同步提交
返回 ACCEPTED 或 PENDING,也只能以本回调作为最终执行结果。
onResyncRequired(expectedSequence, actualSequence)¶
用途:明确通知普通事件队列已经出现不可恢复的缺口,客户端必须重建属性和变量基线。
参数格式:
| 参数 | 含义 |
|---|---|
expectedSequence |
客户端原本应收到的下一事件序号 |
actualSequence |
服务端缺口阶段已经推进到的序号;重同步跟踪可从这里重置 |
Raw AIDL 客户端处理顺序:
- 标记当前增量派生状态不再完整,暂停基于旧基线的计算。
- 将本地序号基准重置为
actualSequence。 - 重新调用
replaceSubscriptions(完整属性集合)。 - 重新调用
replaceVariableSubscriptions(完整变量集合)。 - 若启用了音乐监听,再调用
setMusicSubscription(true)。 - 等待新一轮
INITIAL_SNAPSHOT,再恢复业务计算。
override fun onResyncRequired(expectedSequence: Long, actualSequence: Long) {
callbackExecutor.execute {
sequenceTracker.reset(actualSequence)
session.replaceSubscriptions(desiredProperties)
session.replaceVariableSubscriptions(desiredVariables)
if (musicEnabled) session.setMusicSubscription(true)
}
}
注意事项:即使没有收到本回调,客户端自行发现共享 sequence 跳号也应执行相同流程。
EvccApiClient 已实现缺口检测、整批重放和序号重置;应用仍需在收到新初始快照后重建自己的派生状态。
会话方法和同步返回见IEvccApiSession。
onMusicStateChanged(state, sequence, eventType)(1.1)¶
仅显式开启音乐订阅后触发。首次/重复开启为 INITIAL_SNAPSHOT,后续为 VALUE_CHANGED;sequence 与属性、变量、来源事件共享。音乐状态含当前行和资源版本,不含完整歌词或图片字节。完整歌词版本变化后按需读取;SDK 默认自动加载。音乐 SERVICE_DISABLED 只表示音乐来源未运行,不等同于会话关闭。