与之前 nadcore 框架的本质区别
| 特性 | nadcore SchemeModel | UnitedSchemeEntity |
|---|---|---|
| 分发方式 | 扁平式(直接找 Action) | 树形/层级式(逐级向下分发) |
| 路径解析 | 一次性解析完 | 带游标,逐级解析 |
| 数据流向 | Router → Dispatcher → Action | Dispatcher → SubDispatcher → … → Action |
| 复杂度 | 简单,适合固定模块 | 复杂,适合多层级大型系统 |
核心设计:游标机制
Java
插入复制
/** 路径层级解析位置 (游标) */
private int mPathLevel = -1;
/** 路径数组 */
private String[] mPaths;逐级解析示例
假设 URI 为:baiduboxapp://v1/browser/page/open?url=xxx
插入复制
mPaths = ["v1", "browser", "page", "open"]
↑ ↑
第0级 第3级(Action)分发过程:
插入复制
┌────────────────────────────────────────────────────────────────────┐
│ 第1次 dispatch │
│ ├─ entity.getPath(true) → mPathLevel: -1 → 0, 返回 "v1" │
│ ├─ 找到 V1Dispatcher │
│ └─ 调用 V1Dispatcher.dispatch(entity) │
│ │
│ 第2次 dispatch (V1Dispatcher) │
│ ├─ entity.getPath(true) → mPathLevel: 0 → 1, 返回 "browser" │
│ ├─ 找到 BrowserDispatcher │
│ └─ 调用 BrowserDispatcher.dispatch(entity) │
│ │
│ 第3次 dispatch (BrowserDispatcher) │
│ ├─ entity.getPath(true) → mPathLevel: 1 → 2, 返回 "page" │
│ ├─ 找到 PageDispatcher │
│ └─ 调用 PageDispatcher.dispatch(entity) │
│ │
│ 第4次 dispatch (PageDispatcher) │
│ ├─ entity.getPath(true) → mPathLevel: 2 → 3, 返回 "open" │
│ ├─ entity.isAction() = true (已到最后一级) │
│ └─ 调用 invoke() 执行具体逻辑 │
└────────────────────────────────────────────────────────────────────┘UnitedSchemeEntity 的字段解析
Java
插入复制
public class UnitedSchemeEntity implements Cloneable {
// ======== 核心路由数据 ========
private Uri mUri; // 原始 URI
private String[] mPaths; // 路径数组 ["v1", "browser", "open"]
private int mPathLevel = -1; // 当前解析到第几层(游标)
private HashMap<String, String> mParams; // 查询参数
// ======== 调用上下文 ========
private String mSource; // 调起来源:内部/外部/主入口
private String mReferUrl; // 来源 URL
public String mPageUrl; // 页面 URL(WebView 场景)
private HashMap<String, String> mInvokeInfo; // 额外调用信息
// ======== 状态管理 ========
private boolean mOnlyVerify = false; // 仅验证,不执行
public boolean callbackInvoked = false; // 回调是否已执行
public JSONObject result; // 执行结果
// ======== Clone 支持 ========
UnitedSchemeEntity originEntity; // clone 链,用于追溯原始 entity
}关键方法解读
1. getPath(boolean aheadFirst) - 游标控制
Java
插入复制
public String getPath(boolean aheadFirst) {
if (aheadFirst) {
mPathLevel++; // 先移动游标
}
if (mPathLevel >= 0 && mPathLevel < mPaths.length) {
return mPaths[mPathLevel]; // 返回当前层级的路径
}
return null;
}aheadFirst = true:先移动游标,再返回(分发时用)aheadFirst = false:只返回当前位置(查询时用)
2. isAction() - 判断是否到达最后一层
Java
插入复制
public boolean isAction() {
return mPathLevel == (mPaths.length - 1);
}当游标到达最后一级时,说明已经解析到 Action 层,不再分发。
3. clone() 和 markCallbackInvoked() - 状态追溯
Java
插入复制
public UnitedSchemeEntity clone() {
UnitedSchemeEntity entity = new UnitedSchemeEntity(...);
entity.originEntity = this; // 保存原始引用
return entity;
}
public void markCallbackInvoked() {
callbackInvoked = true;
// 向上追溯,标记整个链路
UnitedSchemeEntity origin = originEntity;
while (origin != null) {
origin.callbackInvoked = true;
origin = origin.originEntity;
}
}用途:当 Entity 被 clone 后传递给子 Dispatcher,回调时需要通知整个调用链。
分发器 (Dispatcher) 的工作流程
Java
插入复制
// UnitedSchemeBaseDispatcher.dispatch()
public boolean dispatch(Context context, UnitedSchemeEntity entity, CallbackHandler handler) {
// 1. 权限校验
boolean valid = checkPermission(context, entity);
if (!valid) return false;
// 2. 安全确认弹窗(可选)
if (needConfirm() && checkConfirm()) {
confirm(context, entity, handler);
return true;
}
// 3. 实际分发
return onDispatcher(context, entity, handler);
}
private boolean onDispatcher(...) {
// 获取下一级路径(游标 +1)
String path = entity.getPath(true);
// 找子 Dispatcher
Class<? extends UnitedSchemeAbsDispatcher> dispatcher = getSubDispatcher(path);
if (dispatcher != null) {
// 继续向下分发
return dispatcher.newInstance().dispatch(context, entity, handler);
} else if (entity.isAction()) {
// 已到 Action 层,执行业务逻辑
return invoke(context, entity, handler);
} else {
// 找不到模块
return false;
}
}架构图
插入复制
┌─────────────────────────────────────────────────────────────────────┐
│ UnitedScheme 分发架构 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ URI: baiduboxapp://v1/browser/page/open?url=xxx │
│ │
│ ┌─────────────────┐ │
│ │ UnitedSchemeEntity │ ← 数据载体 + 游标 │
│ │ mPaths = [v1, browser, page, open] │
│ │ mPathLevel = -1 (初始) │
│ └────────┬────────┘ │
│ │ │
│ ▼ getPath(true) → "v1" │
│ ┌─────────────────┐ │
│ │ RootDispatcher │ │
│ │ getSubDispatcher("v1") → V1Dispatcher │
│ └────────┬────────┘ │
│ │ │
│ ▼ getPath(true) → "browser" │
│ ┌─────────────────┐ │
│ │ V1Dispatcher │ │
│ │ getSubDispatcher("browser") → BrowserDispatcher │
│ └────────┬────────┘ │
│ │ │
│ ▼ getPath(true) → "page" │
│ ┌─────────────────┐ │
│ │ BrowserDispatcher│ │
│ │ getSubDispatcher("page") → PageDispatcher │
│ └────────┬────────┘ │
│ │ │
│ ▼ getPath(true) → "open", isAction() = true │
│ ┌─────────────────┐ │
│ │ PageDispatcher │ │
│ │ invoke() → 执行打开页面逻辑 │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘与 nadcore 框架对比总结
| 维度 | nadcore (SchemeModel) | UnitedScheme (Entity) |
|---|---|---|
| 架构 | 扁平:Module/Action 二级 | 树形:无限层级 |
| 适用场景 | 广告 SDK(Action 固定) | 主 App(功能模块众多) |
| 扩展性 | 添加 Action 简单 | 添加子模块/子 Dispatcher |
| 复杂度 | 低 | 高 |
| 路径示例 | vendor/ad/download | v1/browser/page/open |
| 游标 | ❌ 无 | ✅ mPathLevel |
| Clone链 | ❌ 无 | ✅ originEntity |
一句话总结
UnitedSchemeEntity 是百度 App 主端的层级式路由载体,通过游标机制实现 URI 的逐级解析和分发,支持复杂的模块化架构。
UnitedSchemeEntity 中 三种结果传递机制 的配合使用。
三种结果传递方式
| 方式 | 代码 | 时机 | 用途 |
|---|---|---|---|
| 返回值 | return false | 同步、立即 | 告诉 Dispatcher “我没处理成功” |
| entity.result | entity.result = ... | 同步、立即 | 保存具体错误原因,供上层读取 |
| handler 回调 | handler.onResult(...) | 可异步 | 通知最终调用方(如 JS Bridge) |
为什么需要 entity.result?
看 UnitedSchemeBaseDispatcher.onDispatcher() 中的关键逻辑:
Java
插入复制
private boolean onDispatcher(Context context, UnitedSchemeEntity entity, CallbackHandler handler) {
String path = entity.getPath(true);
int statusCode = UnitedSchemeStatusCode.ERR_OK;
if (!TextUtils.isEmpty(path)) {
Class<? extends UnitedSchemeAbsDispatcher> dispatcher = getSubDispatcher(path);
if (null != dispatcher) {
// 找到子 Dispatcher,继续分发
return dispatcherInstance.dispatch(context, entity, handler);
} else if (!entity.isAction()) {
// 找不到模块
statusCode = UnitedSchemeStatusCode.ERR_MODULE_NOT_FOUND;
}
}
// 子级未能处理时,本级尝试处理
boolean invokeResult = invoke(context, entity, handler);
// ⭐ 关键:根据 entity.result 修正错误码
if (!invokeResult && entity.result != null) {
int invokeStatusCode = entity.result.optInt(UnitedSchemeStatusCode.KEY_STATUS, -1);
// 如果 invoke 返回 "找不到 Action",但实际是 "找不到 Module"
// 则修正为更准确的错误码
if (invokeStatusCode == UnitedSchemeStatusCode.ERR_ACTION_NOT_FOUND
&& statusCode == UnitedSchemeStatusCode.ERR_MODULE_NOT_FOUND) {
entity.result.put(UnitedSchemeStatusCode.KEY_STATUS,
String.valueOf(UnitedSchemeStatusCode.ERR_MODULE_NOT_FOUND));
}
}
return invokeResult;
}图解:entity.result 在分发链中的作用
插入复制
URI: baiduboxapp://v1/browser/invalid_action?xxx
┌────────────────────────────────────────────────────────────────────────┐
│ RootDispatcher.dispatch() │
│ ├─ getSubDispatcher("v1") → V1Dispatcher ✓ │
│ └─ 调用 V1Dispatcher.dispatch() │
│ │
│ V1Dispatcher.dispatch() │
│ ├─ getSubDispatcher("browser") → BrowserDispatcher ✓ │
│ └─ 调用 BrowserDispatcher.dispatch() │
│ │
│ BrowserDispatcher.dispatch() │
│ ├─ getSubDispatcher("invalid_action") → null ✗ │
│ ├─ 记录 statusCode = ERR_MODULE_NOT_FOUND │
│ ├─ 调用 invoke() 尝试本级处理 │
│ │ │
│ │ invoke() 内部: │
│ │ ├─ 找 Action "invalid_action" → 找不到 │
│ │ ├─ entity.result = {status: ERR_ACTION_NOT_FOUND} ← 写入结果 │
│ │ └─ return false │
│ │ │
│ ├─ invokeResult = false │
│ ├─ 读取 entity.result.status = ERR_ACTION_NOT_FOUND │
│ ├─ 但实际是 ERR_MODULE_NOT_FOUND(没有这个子模块) │
│ ├─ ⭐ 修正 entity.result.status = ERR_MODULE_NOT_FOUND │
│ └─ return false │
│ │
│ 最终 entity.result = {status: ERR_MODULE_NOT_FOUND, message: "..."} │
└────────────────────────────────────────────────────────────────────────┘你的代码示例解读
Java
插入复制
private boolean handleAdWebPanel(Context context, UnitedSchemeEntity entity, CallbackHandler handler) {
if (!(context instanceof Activity)) {
// 1. 写入具体错误原因到 entity.result
entity.result = UnitedSchemeUtility.wrapCallbackParams(UnitedSchemeStatusCode.ERR_PARSE_FAIL);
// 2. 返回 false 告诉上层"我没处理成功"
return false;
}
// ... 正常逻辑
}作用:
return false→ 上层 Dispatcher 知道处理失败了entity.result = {...}→ 上层可以读取为什么失败(ERR_PARSE_FAIL:参数解析失败/Context 不对)
为什么不直接用 handler 回调?
Java
插入复制
// ❌ 不推荐这样写
if (!(context instanceof Activity)) {
if (handler != null) {
handler.onResult(false, UnitedSchemeUtility.wrapCallbackParams(ERR_PARSE_FAIL));
}
return false;
}问题:
- handler 可能为 null(不是所有调用都需要回调)
- 回调时机不对:在分发链中间就回调了,上层可能还想做补救处理
- 无法修正错误码:上层读不到具体错误,无法做”ERR_ACTION_NOT_FOUND → ERR_MODULE_NOT_FOUND”这种修正
正确的做法:最终统一回调
Java
插入复制
// 在分发链的最外层(入口处)统一处理回调
public boolean dispatch(Context context, UnitedSchemeEntity entity, CallbackHandler handler) {
boolean result = onDispatcher(context, entity, handler);
// 分发结束后,如果还没回调过,统一回调
if (!entity.isCallbackInvoked() && handler != null) {
handler.onResult(result, entity.result);
entity.markCallbackInvoked();
}
return result;
}总结
插入复制
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ return false → "我没处理成功"(给 Dispatcher) │
│ │
│ entity.result → "具体错误原因"(在分发链中传递和修正) │
│ │
│ handler 回调 → "最终结果通知"(给业务调用方 / JS Bridge) │
│ │
│ 协作流程: │
│ 子层写 entity.result → 上层读取并可能修正 → 最外层统一 handler 回调 │
│ │
└─────────────────────────────────────────────────────────────────────┘简单说:entity.result 是分发链内部的”错误信息传递槽”,让上层有机会读取、修正错误,最后再统一回调给外部。
更新: 2026-01-23 15:34:22
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/epewqf2xq82y2z4u