如何编写自定义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
相关产品推荐
相关产品推荐

