惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
量子位
M
MIT News - Artificial intelligence
Y
Y Combinator Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Google DeepMind News
Google DeepMind News
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
雷峰网
雷峰网
I
InfoQ
罗磊的独立博客
博客园 - 聂微东
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
D
Docker
H
Hackread – Cybersecurity News, Data Breaches, AI and More
腾讯CDC
博客园 - 三生石上(FineUI控件)
The GitHub Blog
The GitHub Blog
K
Kaspersky official blog
P
Privacy & Cybersecurity Law Blog
S
SegmentFault 最新的问题
T
Threat Research - Cisco Blogs
H
Help Net Security
小众软件
小众软件
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
C
CERT Recently Published Vulnerability Notes
WordPress大学
WordPress大学
T
Tenable Blog
T
The Blog of Author Tim Ferriss
C
Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - Franky
A
Arctic Wolf
T
Threatpost
Scott Helme
Scott Helme
C
Cybersecurity and Infrastructure Security Agency CISA
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
The Exploit Database - CXSecurity.com
G
GRAHAM CLULEY
Security Latest
Security Latest
Spread Privacy
Spread Privacy
L
LINUX DO - 热门话题
V
Vulnerabilities – Threatpost
P
Privacy International News Feed
S
Schneier on Security
Latest news
Latest news
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com

美团技术团队

美团 · 技术团队 美团 · 技术团队 美团 · 技术团队 美团 · 技术团队 美团 · 技术团队 美团 · 技术团队 美团发布基于 N-gram 全新模型:嵌入扩展新范式,实现轻量化 MoE 高效进化 2025美团技术年货,「马」上到来 多维创新打造强泛化智能体模型,LongCat-Flash-Thinking-2601技术报告发布 美团 EvoCUA 刷新开源 SOTA,会用电脑还会持续进化的智能体! 美团 LongCat-Flash-Thinking-2601 发布,工具调用能力登顶开源 SOTA! KuiTest:基于大模型通识的 UI 交互遍历测试 AAAI 2026 | 美团技术团队学术论文精选 2025 | 美团技术团队热门技术文章汇总 美团 LongCat-Video-Avatar 正式发布,实现开源 SOTA 级拟真表现 大模型剪枝新范式:先浓缩,再剪枝——DenoiseRotator技术解读 LongCat 上线 AI 生图!精准高效,AI 创作不设限 美团发布 LongCat-Image 图像生成模型,编辑能力登顶开源 SOTA AI Coding与单元测试的协同进化:从验证到驱动 R-HORIZON:探索长程推理边界,复旦NLP&美团LongCat联合提出LRMs能力评测新框架 美团 LongCat 发布 AMO-Bench:突破 AIME 评测饱和困境,重新定义 LLM 数学上限 美团 LongCat Interaction 团队发布大模型交互系统技术报告 WOWService 美团 LongCat 团队发布全模态一站式评测基准 UNO-Bench 美团开源LongCat-Audio-Codec,高效语音编解码器助力实时交互落地 NeurIPS 2025 | 美团技术团队论文精选 LongCat-Flash-Omni正式发布并开源:开启全模态实时交互时代 美团 LongCat 团队发布 VitaBench:基于复杂生活场景的交互式 Agent 评测基准 LongCat-Video 视频生成模型正式发布,探索世界模型的第一步 ICCV 2025 | 美团论文精选及多模态推理竞赛冠军方法分享 从0到1建设美团数据库容量评估系统 可验证过程奖励在提升大模型推理效率中的探索与实践 LongCat-Flash-Thinking 正式发布,更强、更专业,保持极速! 开源 | InfiniteTalk:无限长虚拟人视频生成的新范式 美团正式发布并开源 LongCat-Flash-Chat,动态计算开启高效 AI 时代 美团 M17 团队开源 Meeseeks 评测集:揭秘大模型的“听话”能力 可信实验白皮书系列08:开放式分析引擎 | 附PDF合集 美团智能头盔研发实践系列02:软件功能篇 美团智能头盔研发实践系列01:硬件设计篇 ACL 2025 | 美团技术团队论文精选 美团开源OIBench与CoreCodeBench:揭示大模型编程能力的真实水平 可信实验白皮书系列07:高阶实验工具 开源 | MeiGen-MultiTalk:基于单张照片实现多人互动演绎 可信实验白皮书系列06:观察性研究 JDK高版本特性总结与ZGC实践 可信实验白皮书系列05:准实验 可信实验白皮书系列04:随机轮转实验 可信实验白皮书系列03:随机对照实验 可信实验白皮书系列02:AB实验基础 可信实验白皮书系列01:从0到1的方法论与实践指南 MTGR:美团外卖生成式推荐Scaling Law落地实践 OR算法+ML模型混合推理框架架构演进 ICLR&CVPR 2025美团技术团队论文精选 行为正则化与顺序策略优化结合的离线多智能体学习算法 预测技术在美团弹性伸缩场景的探索与应用 美团技术年货 | 600+页电子书,算法、工程、测试、数据、安全系列大合集 鸿蒙应用签名实操及机制探究 2024 | 美团技术团队热门技术文章汇总 AutoConsis:UI内容一致性智能检测 CIKM 2024 | 美团技术团队精选论文解读 大前端:如何突破动态化容器的天花板? KDD 2024 OAG-Challenge Cup赛道三项冠军技术方案解读 新一代实验分析引擎:驱动履约平台的数据决策 大众点评技术部包揽KDD 2024 OAG-Challenge Cup赛道全部3项冠军 ACL 2024 | 美团技术团队精选论文解读 KDD 2024 | 美团技术团队精选论文解读 基本功 | 一文讲清多线程和多线程同步 小程序可测性能力建设与实践 SIGIR 2024 | 美团技术团队精选论文解读 Spark向量化计算在美团生产环境的实践 CVPR 2024 | 美团技术团队精选论文解读 基于多模态信息抽取的菜品知识图谱构建 DDD在大众点评交易系统演进中的应用 美团外卖基于GPU的向量检索系统实践 美团大规模KV存储挑战与架构实践 基于接口数据变异的App健壮性测试实践 美团技术年货 | 600+页电子书,前端、后端、算法、测试、运维系列大合集 美团RASP大规模研发部署实践总结 2023 | 美团技术团队热门技术文章汇总 美团到店终端从标准化到数字化的演进之路 AIOps在美团的探索与实践——事件管理篇 美团技术博客十周年,感谢一路相伴 基于UI交互意图理解的异常检测方法 如何利用「深度上下文兴趣网络」提升点击率? 基于模式挖掘的可靠性治理探索 代码变更风险可视化系统建设与实践 美团多场景建模的探索与实践 MJDK 如何实现压缩速率的 5 倍提升? 如何提供一个可信的AB测试解决方案 KDD 2023 | 美团技术团队精选论文解读 美团前端研发框架Rome实践和演进趋势 斩获CVPR 2023竞赛2项冠军|美团街景理解中视觉分割技术的探索与应用 MySQL自治平台建设的内核原理及实践(下) CVPR 2023 | 美团技术团队精选论文解读 超大规模数据库集群保稳系列之三:美团数据库容灾体系建设实践 超大规模数据库集群保稳系列之二:数据库攻防演练建设实践 Robust 2.0:支持Android R8的升级版热修复框架 超大规模数据库集群保稳系列之一:高可用系统 一次「找回」TraceId的问题分析与过程思考 基于AI+数据驱动的慢查询索引推荐 SOTA!目标检测开源框架YOLOv6 3.0版本来啦
Android自定义Lint实践2——改进原生Detector
陈潼 建帅 · 2017-03-09 · via 美团技术团队

上篇博客《Android自定义Lint实践》中我们介绍了美团App如何使用自定义Lint进行代码检查。

在使用Lint的过程中,我们陆续又发现原生Lint的一些问题和缺陷,本文将介绍我们在实践中提出的解决方案。

上一篇博客中我们提到了对于HashMap检测的改进,但当时我们也在文章中提到:

代码很简单,总体就是获取变量定义的地方,将泛型值传入原先的检测逻辑。 当然这里的增强也是有局限的,比如这个变量是成员变量,向前的推断就会有问题,这点我们还在持续的优化中。

即:当时的检测解决了变量声明和变量赋值在一起的HashMap检测问题。但对于两者不在一起的情况,我们仍然无法检测到。

示例代码如下:

public static void testHashMap() {
	//这种情况可以用上篇博客的检查搞定
    Map<Integer, String> map1 = new HashMap<>();
    map1.put(1, "name");
    //这种找不到map2的变量声明,所以用上篇博客的检查是无法判断的
    map2 = new HashMap<>();
    map2.put(2, "name2");
}

通过我们的探索,目前已经解决了这个问题。

下面我们来详细介绍下:

我们需要解决的情况

  1. 在同一个类中 java public Map<Integer, String> map; public static Map<Integer, String> map2; public void test() { // 1: 成员变量 map = new HashMap<>(); map.put(1, "name"); // 2: 静态变量 map2 = new HashMap<>(); map2.put(1, "name"); }
  2. 方法参数

    public void test1(Map<Integer, String> map) {
        map = new HashMap<>();
        map.put(1, "name");
    }
    
  3. 变量声明在另一个类中

    public class HashMapCase4_2 {
        public void test() {
            // 1: 另一个类的静态变量
            HashMapCase4_1.map2 = new HashMap<>();
            HashMapCase4_1.map2.put(1, "name");
            // 2: 另一个对象的成员变量
            HashMapCase4_1 case4_1 = new HashMapCase4_1();
            case4_1.map = new HashMap<>();
            case4_1.map.put(1, "name");
            // 3: 内部类静态变量
            Sub.map2 = new HashMap<>();
            // 4: 内部类对象的成员变量
            Sub sub = new Sub();
            sub.map = new HashMap<>();
        }
        private static class Sub {
            public Map<Integer, String> map;
            public static Map<Integer, String> map2;
        }
    }
    

解决方案

在Google官方提供的资料:Writing a Lint Check中我们发现了如下描述:

In the next version of lint (Tools 27, Gradle plugin 0.9.2+, Android Studio 0.5.3, and ADT 27); Java AST parse tree detectors can both resolve types and declarations. This was just added to lint, and offers new APIs where you can ask for the resolved type, and the resolved declaration, of a given AST node.

这里提到了resolved type,那究竟有什么用呢?

Google在描述中留下当时的commit,其中提到:

Add type and declaration resolution to Lint’s Java AST

The AST used by lint, Lombok AST, does not contain type information. That means code which for example sees this code:

getContext().checkPermission(name)

can’t find out which “checkPermission” method this is. That requires full type resolution.

根据官方描述,我们可以拿到方法属于哪个类。那resolved type是否可以帮助我们通过变量拿到变量声明呢?

在参考了commit中的代码后,我们尝试使用context.resolve来解析第一种情况中的变量map

结果证实确实帮我们解析到了变量声明的类型。

但它可以帮我们把所有情况都分析到么?我们带着怀疑的态度继续尝试,结果发现在第三种情况的case4_1.mapsub.map出现了问题:

即只分析到了map所属的对象,而无法拿到map的类型。

显然,这个解析出来的节点不仅没有帮助我们,反而让我们偏离了我们要分析的节点。

在查看JavaContext相关代码后我们发现,除了resolve还有一个getType方法,似乎从名字上看可以解决我们的问题。

@Nullable
public ResolvedNode resolve(@NonNull Node node) {
    return mParser.resolve(this, node);
}
@Nullable
public TypeDescriptor getType(@NonNull Node node) {
    return mParser.getType(this, node);
}

尝试后发现,getType适合我们列出的所有情况。

那么,两者区别是什么呢?

通过对Android Gradle Plugin(下文中称Plugin)中Lint相关代码的分析,我们发现:
在Plugin中,Lint检查依靠ECJ(Eclipse Compiler for Java)来生成抽象语法树,上文代码中提到的mParser在Plugin中对应的是EcjParser

解析时,对于case4_1.mapsub.map两个节点,resolve利用的是binding,而getType调用的是resolvedType(注意:这里的resolvedType是ECJ中的变量)。

Bindings是ECJ一个强大的功能,有很多子类型,例如VariableBindingTypeBinding等。

对于同一个节点可能还有多个binding(例如QualifiedNameReferenceotherBindings会存放多个,上述例子中可以看到其实有case4_1.mapmap类型,但在otherBindings中);而resolvedTypeTypeBinding。显然,使用resolvedType可以确保我们拿到的是类型。

这里还需要注意的是:虽然上述分析中,我们提到的这些是由ECJ提供的,且Lint中的Node也保留了拿到ECJ Node的能力,即:getNativeNode。但并不推荐大家直接使用ECJ。

因为Lint使用tnorbye/lombok.ast的本意就是不依赖具体的Parser(Writing a Lint Check中提到,他们曾经使用了多种parser),上层Detector应尽量使用Lombok AST。

美团App使用了Retrolambda,当然为了在Retrolambda下Lint能正常运行,我们引入了evant/android-retrolambda-lombok,替换官方AST(抽象语法树)为Retrolambda实现的AST。

但在lambda中写Toast经常会提示没有show, 示例如下:

public void test() {
	findViewById(R.id.button).setOnClickListener(view -> Toast.makeText(MainActivity.this, "xxx", Toast.LENGTH_SHORT).show());
}

Lint检查报告:Toast created but not shown: did you forget to call show() ?

从代码可以看到,虽然我们写了show,但还是检测说没有show。

这时候如果把Toast相关的代码抽离成单独的方法,检测就又会恢复正常。于是我们决定分析下究竟发生了什么?

通过gradle debug,我们发现ToastDetector在寻找包围Toast方法时出现了问题。

Node method1 = JavaContext.findSurroundingMethod(node.getParent());

而findSurroundingMethod方法的实现如下:

@Nullable
public static Node findSurroundingMethod(Node scope) {
    while (scope != null) {
        Class<? extends Node> type = scope.getClass();
        // The Lombok AST uses a flat hierarchy of node type implementation classes
        // so no need to do instanceof stuff here.
        if (type == MethodDeclaration.class || type == ConstructorDeclaration.class) {
            return scope;
        }
        scope = scope.getParent();
    }
    return null;
}

到这里总结一下:
当ToastDetector找到Toast的时候,它会寻找外围的方法,如果是匿名内部类的方法或者其他方法时,他能够判断到并返回这个节点。

但是对于lambda来说,它只能查找到最外层的方法,也就是示例中setOnClickListener外围的test方法,lambda并不会被识别到。

lambda在语句附近能识别到的是lombok.ast.LambdaExpression,而不是MethodDeclaration或者ConstructorDeclaration,所以会一直找到test这个MethodDeclaration

问题搞清楚了,解决办法也就有了:
我们加入一个LambdaExpression判断,提前返回,这样就可以正常识别了。

private static boolean isLambdaExpression(Class type) {
    return "lombok.ast.LambdaExpression".equals(type.getName());
}

这里需要说明的是,我们用字符串比对而不是跟MethodDeclaration一样去比对class,这是为了更好的兼容所有使用者。

因为LambdaExpression是由Retrolambda的AST提供,并不是官方的AST。也就是说如果我们想判断class就必须依赖Retrolambda的AST,我们之前也提到过自定义Lint输出的是一个JAR,并不包含这些依赖,运行时环境中如果没有使用Retrolambda AST的话就会直接ClassNotFound。

所以,这里我们选择了字符串比对,达成目标的同时,也让检测变得更简单。

Detector写好了,但是与HashMap的增强不同,ToastDetector这个实现只能选择替换掉系统实现。因为HashMap两者是增强,可以共存;而ToastDetector如果系统检测正常运行的话,遇到这种情况就会报错。所以我们反射修改内置IssueRegistry(BuiltinIssueRegistry) 完成系统Detector的替换。

本文相关示例源码已经开放,见:MeituanLintDemo

陈潼,美团Android高级工程师,效力于美团平台技术部。2015年加入美团,先后负责代码静态检查、网络层优化等基础设施开发工作。

美团平台技术部客户端技术团队,负责美团平台的基础业务和移动基础设施的开发工作。基于海量用户的美团平台,支撑了美团多条业务线的快速发展。同时,我们也在移动开发技术方面做了一些积极的探索,在动态化、质量保障、开发模型等方面有一定积累。客户端技术团队积极采用开源技术的同时,也把我们的一些积累回馈给开源社区,希望跟业界一起推动移动开发效率、质量的提升。

最后来个硬广告:客户端技术团队长期招聘技术专家,有兴趣的同学可以发送简历到:fangjintao#meituan.com。