# 直接调用与 override 相关引用

定位单个方法时使用完整 descriptor，包含参数类型和返回类型。仅按类名/方法名过滤会包含所有重载。查询 `Runnable.run()V` 的调用者：

```sql
SELECT DISTINCT file_id, method_idx, method
FROM method_calls
WHERE callee = 'Ljava/lang/Runnable;->run()V';
```

这会找到指令中引用该接口方法的调用，即使接口定义位于输入 APK 之外也可以。指令引用 `Lcom/example/Task;->run()V` 时，对应的是另一个目标。`method_calls` 每条调用指令占一行：`count(*)` 统计调用次数，上面的 `DISTINCT` 投影则返回按 `(file_id, method_idx)` 标识的调用者方法。

如果已经确认 `Task.run()V` 实现了该接口方法，可以一起查询选定目标。将 `Task` 替换为要检查的实现类：

```sql
SELECT DISTINCT file_id, method_idx, method
FROM method_calls
WHERE callee IN (
    'Ljava/lang/Runnable;->run()V',
    'Lcom/example/Task;->run()V'
);
```

发现候选方法时，将直接接口声明与 virtual 方法定义关联。join 保留 `file_id`，避免混用不同 DEX 的类索引：

```sql
SELECT DISTINCT m.descriptor
FROM methods m
JOIN class_interfaces i
  ON m.file_id = i.file_id AND m.class_idx = i.class_idx
WHERE i.interface_descriptor = 'Ljava/lang/Runnable;'
  AND m.name = 'run'
  AND m.param_count = 0
  AND m.return_descriptor = 'V'
  AND m.method_kind = 'virtual';
```

调用方可以从结果中选取候选方法，并在同一 dataset 上复用得到的 descriptor 集合。统计指向任一候选方法或接口方法本身的调用记录数：

```sql
WITH targets AS (
    SELECT 'Ljava/lang/Runnable;->run()V' AS descriptor
    UNION
    SELECT m.descriptor
    FROM methods m
    JOIN class_interfaces i
      ON m.file_id = i.file_id AND m.class_idx = i.class_idx
    WHERE i.interface_descriptor = 'Ljava/lang/Runnable;'
      AND m.name = 'run'
      AND m.param_count = 0
      AND m.return_descriptor = 'V'
      AND m.method_kind = 'virtual'
)
SELECT count(*) AS call_count
FROM method_calls
WHERE callee IN (SELECT descriptor FROM targets);
```

覆盖范围与查询成本：

- 候选查询找到的是在接口列表中**直接**声明 `Runnable` 的类型自己定义的 `run()V` 方法。`method_kind = 'virtual'` 排除了 static/private 方法和仅引用行；它不要求存在具体方法体，抽象声明也可以是调用目标。
- 间接接口实现、子接口以及从父类继承的方法，需要沿 `classes.superclass_descriptor` 和 `class_interfaces` 进一步遍历，再解析对应方法。调用方可以迭代查询这些关系，对已访问的类型 descriptor 去重，按签名和访问规则构造目标集合。有参数的方法要匹配参数类型及顺序，不能只比较 `param_count`。
- 组合查询统计选定方法的引用数，不表示某次接口调用一定执行哪个实现。使用选定目标查询调用者时，如果需要保留不同调用目标，可同时投影 `callee`，得到去重后的调用者/目标对。
- 按需展开目标。只查直接调用者时优先使用精确 descriptor，目标已知时使用选定的 `IN` 列表。全候选 CTE 在大型 APK 上可能生成很大的目标集合，查询成本显著增加；它不是持久缓存。将计数改为返回完整的 `DISTINCT` 调用者列表，还可能触发中间结果内存预算。大范围查找调用者时，可按实测确定的批量大小查询选定目标，再在调用方按 `(file_id, method_idx)` 合并去重。重复查询可在同一 dataset 内复用已发现的目标，高频使用前先测量组合查询成本。
- 跨 DEX 匹配目标仍使用完整 descriptor。定义处的 `method_idx` 只属于该 DEX，其他 DEX 中的引用有自己的索引。这些示例使用现有 schema，仅在查询要求时展开目标。

---

Copyright (c) 2026 LSPosed Developers。本文档采用 [MIT 许可证](../license.md)。
