与之前 nadcore 框架的本质区别

特性nadcore SchemeModelUnitedSchemeEntity
分发方式扁平式(直接找 Action)树形/层级式(逐级向下分发)
路径解析一次性解析完带游标,逐级解析
数据流向Router → Dispatcher → ActionDispatcher → 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/downloadv1/browser/page/open
游标❌ 无✅ mPathLevel
Clone链❌ 无✅ originEntity

一句话总结

UnitedSchemeEntity 是百度 App 主端的层级式路由载体,通过游标机制实现 URI 的逐级解析和分发,支持复杂的模块化架构。

UnitedSchemeEntity 中 三种结果传递机制 的配合使用。


三种结果传递方式

方式代码时机用途
返回值return false同步、立即告诉 Dispatcher “我没处理成功”
entity.resultentity.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;
    }
    // ... 正常逻辑
}

作用:

  1. return false → 上层 Dispatcher 知道处理失败了
  2. 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;
}

问题:

  1. handler 可能为 null(不是所有调用都需要回调)
  2. 回调时机不对:在分发链中间就回调了,上层可能还想做补救处理
  3. 无法修正错误码:上层读不到具体错误,无法做”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