Lint实现原理

一.LintTask

apply plugin: 'com.android.library'
apply plugin: 'com.android.application'

当我们在gradle中,apply一个library或者application的plugin时,对应的其实是一个LibraryPlugin或AppPlugin对象,他们都继承自BasePlugin,在BasePlugin的apply()方法中,会调用createTasks()方法:

private void createTasks() {
   
    //...
    project.afterEvaluate(
            project -> {
   
                sourceSetManager.runBuildableArtifactsActions();
                threadRecorder.record(
                        ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
                        project.getPath(),
                        null,
                        () -> createAndroidTasks());
            });
}

在gradle都执行完(afterEvaluate)后,调用createAndroidTasks方法,最终会调用到VariantManager的createTasksForVariantScope抽象方法,为每一个variant创建Tasks:

for (final VariantScope variantScope : variantScopes) {
   
    createTasksForVariantData(variantScope);
}

无论TaskManager是LibraryTaskManager还是ApplicationTaskManager类型,都会在该方法中调用createLintTasks方法,创建Lint的Task:

public void createLintTasks(final VariantScope scope) {
   
    //...
    taskFactory.register(new LintPerVariantTask.CreationAction(scope, variantScopes));
}

LintPerVariantTask.CreationAction:

public static class CreationAction extends BaseCreationAction<LintPerVariantTask> {
   
    //...
    @Override
    @NonNull
    public String getName() {
   
        //generate task name->lintMusicallyI18nDebug
        return scope.getTaskName("lint");
    }

    @Override
    @NonNull
    public Class<LintPerVariantTask> getType() {
   
        //create new instance by reflect
        return LintPerVariantTask.class;
    }

    @Override
    public void configure(@NonNull LintPerVariantTask lint) {
   
        //...
}
  1. create时,通过getType()返回的Class对象,反射创建Task对象,添加到TaskManager里
  2. Task的name,由scope产生,lint拼接scope的variant name,如lintMusicallyI18nDebug
  3. 最后调用CreationAction的configure方法进行Task的配置

二.LintOptions配置

lint相关的配置,我们需要配置在gradle中,通过extension的形式定义:

android {
   
    lintOptions {
   
        //checked rules
        check 'HardcodedText'
        //results
        xmlReport true
        xmlOutput file("lint-results.xml")
    }
}

1.创建

同样是BasePlugin的apply()方法中,进行了Extension的初始化:

threadRecorder.record(
        ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,
        project.getPath(),
        null,
        this::configureExtension);

private void configureExtension() {
   
    extension =
        createExtension(
                project,
                ...);
    globalScope.setExtension(extension);
}
  1. 首先调用了抽象方法configureExtension(),每种Plugin进行自己的Extension初始化
  2. 把生成的Extension对象放入globalScope中
    LibrayPlugin的configureExtension():
protected BaseExtension createExtension(@NonNull Project project, ...) {
   
    return project.getExtensions()
            .create(
                    "android",
                    getExtensionClass(),
                    ...);
}

其中,"android"作为Extension的名字,然后getExtensionClass()返回的Class对象,会通过反射方式创建一个实例,作为Extension对象,LibraryPlugin的该方法,返回的是LibraryExtension.class,在LibraryExtension初始化时,会创建一个LintOptions对象:
lintOptions = objectFactory.newInstance(LintOptions.class);

2.配置

在gradle解析时,会将我们配置的lintOptions选项,配置到LintOptions对象中:

public void lintOptions(Action<LintOptions> action) {
   
    action.execute(lintOptions);
}

3.获取

在CreationAction的configure方法配置Task时,从globalScope中的Extension,取出LintOptions,设置给Task:

public void configure(@NonNull T lintTask) {
   
    lintTask.lintOptions = globalScope.getExtension().getLintOptions();
    //...
}

三.Lint执行触发

ReflectiveLintRunner

LintPerVariantTask作为一个Task,执行时会调用标有@TaskAction注解的方法:

@TaskAction
public void lint() {
   
    //LintPerVariantTaskDescriptor是Task的一些参数,包括LintOptions可以从中获取
    runLint(new LintPerVariantTaskDescriptor());
}

protected void runLint(LintBaseTaskDescriptor descriptor) {
   
    FileCollection lintClassPath = getLintClassPath();
    if (lintClassPath != null) {
   
        new ReflectiveLintRunner().runLint(getProject().getGradle(),
                descriptor, lintClassPath.getFiles());
    }
}
  1. getLintClassPath:Lint相关依赖,在BasePlugin的apply()时进行初始化
public static void createLintClasspathConfiguration(@NonNull Project project) {
   
    Configuration config = project.getConfigurations().create(LintBaseTask.LINT_CLASS_PATH);
    //...
    project.getDependencies().add(config.getName(), "com.android.tools.lint:lint-gradle:" +
            Version.ANDROID_TOOLS_BASE_VERSION);
}

lint相关的依赖:com.android.tools.lint:lint-gradle:26.2.1
执行时,调用ReflectiveLintRunner的runLint()方法:

fun runLint(gradle: Gradle, request: LintExecutionRequest, lintClassPath: Set<File>) {
   
    try {
   
        val loader = getLintClassLoader(gradle, lintClassPath)
        val cls = loader.loadClass("com.android.tools.lint.gradle.LintGradleExecution")
        val constructor = cls.getConstructor(LintExecutionRequest::class.java)
        val driver = constructor.newInstance(request)
        val analyzeMethod = driver.javaClass.getDeclaredMethod("analyze")
        analyzeMethod.invoke(driver)
    } catch (e: InvocationTargetException) {
   
        if (e.targetException is GradleException) {
   
            // Build error from lint -- pass it on
            throw e.targetException
        }
        throw wrapExceptionAsString(e)
    } catch (t: Throwable) {
   
        // Reflection problem
        throw wrapExceptionAsString(t)
    }
}

这里需要说明:

  1. ReflectiveLintRunner、LintExecutionRequest这些类是属于lint-gradle-api这个依赖的
  2. 具体的实现类如LintGradleExecution,属于lint-gradle这个依赖
  3. gradle插件直接依赖了lint-gradle-api,没有直接依赖lint-gradle,所以需要通过反射方式调用LintGradleExecution

LintGradleExecution

public void analyze() throws IOException {
   
    if (toolingRegistry != null) {
   
        //...
        String variantName = descriptor.getVariantName();
        if (variantName != null) {
   
            for (Variant variant : modelProject.getVariants()) {
   
                if (variant.getName().equals(variantName)) {
   
                    lintSingleVariant(variant);
                    return;
                }
            }
        }...
    }...
}

public void lintSingleVariant(@NonNull Variant variant) {
   
    //...
    runLint(variant, variantInputs, true, true, true);
}

private Pair<List<Warning>, LintBaseline> runLint(
        @Nullable Variant variant,
        @NonNull VariantInputs variantInputs,
        boolean report,
        boolean isAndroid,
        boolean allowFix) {
   
    //创建Android的默认ISSUES
    IssueRegistry registry = createIssueRegistry(isAndroid);
    //创建Lint里需要的配置model
    LintCliFlags flags = new LintCliFlags();
    //创建Lint执行器的对象
    LintGradleClient client =
            new LintGradleClient(
                    descriptor.getGradlePluginVersion(),
                    registry,
                    flags,
                    ...);
    //...
    LintOptions lintOptions = descriptor.getLintOptions();
    boolean fix = false;
    if (lintOptions != null) {
   
        //同步LintOptions到LintCliFlags中
        syncOptions(
                lintOptions,
                client,
                flags,
                ...);
    }...
    //...
    try {
   
        //执行Lint检测
        warnings = client.run(registry);
    } catch (IOException e) {
   
        //...
    }
    //如果检测有问题且配置中允许report,则抛出GradleException终止命令的执行
    if (report && client.haveErrors() && flags.isSetExitCode()) {
   
        abort(client, warnings.getFirst(), isAndroid);
    }
    return warnings;
}
  1. 创建Android默认的规则ISSUE(如对TextView、R.id的检测),这里的ISSUES是每个lint规则需要定义的一个对象,将规则实现时会细说
private static BuiltinIssueRegistry createIssueRegistry(boolean isAndroid) {
   
    if (isAndroid) {
   
        return new BuiltinIssueRegistry();
    }...
}
//BuiltinIssueRegistry
static {
   
    List<Issue> issues = new ArrayList<>(INITIAL_CAPACITY);
    issues.add(MissingIdDetector.ISSUE);
    issues.add(TextViewDetector.ISSUE);
    //...
}
  1. LintCliFlags为lint执行器的配置项,只需要将LintOptions的配置项设置进来即可
  2. LintGradleClient为lint检测的执行器,继承自LintCliClient,run方法为执行lint检测的入口
  3. 检测结束后,如果有检测出来的error以及需要report,进行abort,实质就是抛出一个GradleException

LintGradleClient(extends LintCliClient)

public int run(@NonNull IssueRegistry registry, @NonNull List<File> files) throws IOException {
   
    //...
    //创建LintRequest,获取Project
    LintRequest lintRequest = createLintRequest(files);
    //创建LintDriver---Lint检测解析器
    driver = createDriver(registry, lintRequest);
    //...
    //执行真正的Lint分析
    driver.analyze();
    //输出结果,html、xml
    for (Reporter reporter : flags.
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值