You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写自定义Android Lint规则检测RxJava2调用结果未被使用的场景?

实现RxJava2 Disposable未使用检测的Lint规则

嘿,这个需求特别实用——RxJava2里忘了处理subscribe()返回的Disposable确实是内存泄漏的重灾区,用Lint在构建阶段就把这个问题掐灭太明智了。我来给你一步步拆解怎么实现这个规则:

1. 核心检测逻辑

咱们要抓的就是直接调用subscribe()但完全不理会返回值的场景:

  • 放过那些把返回值传给其他方法的情况(比如compositeDisposable.add(observable.subscribe(...)))
  • 放过把返回值赋值给变量的情况(比如Disposable d = observable.subscribe(...))
  • 其他情况(直接写observable.subscribe(...)不处理返回值)直接标记成错误,让构建失败

2. Lint规则实现步骤

2.1 先搭好Lint模块

如果你的项目还没有专门的Lint模块,先建一个,然后在模块的build.gradle里加依赖:

dependencies {
    compileOnly "com.android.tools.lint:lint-api:30.1.2"
    compileOnly "com.android.tools.lint:lint-checks:30.1.2"
}

版本号可以根据你项目的Android Gradle Plugin版本调整,保持兼容就行。

2.2 编写Detector核心代码

创建一个继承自JavaScanner的Detector类,这是Lint规则的核心逻辑:

public class RxJavaDisposableDetector extends Detector implements Detector.JavaScanner {

    // 只关注subscribe方法
    private static final List<String> TARGET_METHODS = Arrays.asList("subscribe");

    @Override
    public List<String> getApplicableMethodNames() {
        return TARGET_METHODS;
    }

    @Override
    public void visitMethodCall(Context context, JavaContext javaContext, AstVisitor visitor, MethodInvocation node) {
        // 先确认这个subscribe方法返回的是Disposable
        PsiMethod method = node.resolveMethod();
        if (method == null) return;

        PsiClass returnTypeClass = method.getReturnType() instanceof PsiClassType 
            ? ((PsiClassType) method.getReturnType()).resolve() 
            : null;
        if (returnTypeClass == null || !"io.reactivex.disposables.Disposable".equals(returnTypeClass.getQualifiedName())) {
            return;
        }

        // 再确认调用者是RxJava2的被观察者类型(Observable/Flowable这些)
        PsiExpression receiver = node.getMethodExpression().getQualifierExpression();
        if (receiver == null) return;
        PsiType receiverType = receiver.getType();
        if (receiverType instanceof PsiClassType) {
            PsiClass receiverClass = ((PsiClassType) receiverType).resolve();
            if (receiverClass == null || !isRxObservableClass(receiverClass.getQualifiedName())) {
                return;
            }
        }

        // 最后检查返回值有没有被使用
        if (!isReturnValueUsed(node)) {
            javaContext.report(
                ISSUE,
                node,
                javaContext.getLocation(node),
                "RxJava2 subscribe() 返回的Disposable未妥善保存!请将它赋值给变量,或者添加到CompositeDisposable中,避免内存泄漏。"
            );
        }
    }

    // 判断是不是RxJava2的被观察者类
    private boolean isRxObservableClass(String qualifiedName) {
        return qualifiedName != null && (
            qualifiedName.startsWith("io.reactivex.Observable") ||
            qualifiedName.startsWith("io.reactivex.Flowable") ||
            qualifiedName.startsWith("io.reactivex.Single") ||
            qualifiedName.startsWith("io.reactivex.Completable") ||
            qualifiedName.startsWith("io.reactivex.Maybe")
        );
    }

    // 判断返回值是否被合理使用
    private boolean isReturnValueUsed(MethodInvocation node) {
        PsiElement parent = node.getParent();
        // 情况1:作为参数传给其他方法(比如add到CompositeDisposable)
        if (parent instanceof PsiExpressionList) {
            return true;
        }
        // 情况2:赋值给变量
        if (parent instanceof PsiAssignmentExpression) {
            return true;
        }
        // 情况3:作为返回值返回
        if (parent instanceof PsiReturnStatement) {
            return true;
        }
        // 其他情况就是没用到返回值
        return false;
    }

    // 定义这个Lint问题的元数据
    public static final Issue ISSUE = Issue.create(
        "RxJavaDisposableUnused",
        "RxJava2 Disposable未妥善保存",
        "RxJava2中subscribe()返回的Disposable用于取消订阅,不保存的话没法在合适时机取消,很容易引发内存泄漏。",
        Category.CORRECTNESS,
        10, // 优先级拉满,优先检测
        Severity.ERROR, // 设为ERROR会直接让构建失败
        new Implementation(
            RxJavaDisposableDetector.class,
            Scope.JAVA_FILE_SCOPE
        )
    );
}

2.3 注册你的Lint规则

创建一个IssueRegistry子类,把刚才写的Detector注册进去:

public class CustomLintRegistry extends IssueRegistry {
    @Override
    public List<Issue> getIssues() {
        return Arrays.asList(RxJavaDisposableDetector.ISSUE);
    }
}

然后在Lint模块的build.gradle里配置这个registry:

jar {
    manifest {
        attributes("Lint-Registry-v2": "com.yourpackage.CustomLintRegistry")
    }
}

记得把com.yourpackage换成你自己的包名。

2.4 把规则集成到主项目

在主项目的build.gradle里添加Lint模块的依赖:

dependencies {
    lintChecks project(':your-lint-module-name')
}

替换成你实际的Lint模块名称就行。

3. 测试一下

用你给的示例代码测试:

final Observable<String> observable = getObservable(); 
observable.subscribe(this::onSuccess, this::onError); // 这里会触发Lint错误,构建直接失败

而下面这些合法的写法完全不会被检测:

// 保存到变量
Disposable disposable = observable.subscribe(this::onSuccess, this::onError);

// 添加到CompositeDisposable
CompositeDisposable composite = new CompositeDisposable();
composite.add(observable.subscribe(this::onSuccess, this::onError));

// 作为返回值返回
public Disposable setupObservable() {
    return getObservable().subscribe(this::onSuccess, this::onError);
}

内容的提问来源于stack exchange,提问作者oggmonster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:50:59