resourcePrefix深入解析

时间:2025-01-27 07:42:01
public class ResourcePrefixDetector extends ResourceXmlDetector implementsDetector.BinaryResourceScanner { /** The main issue discovered by this detector */ @SuppressWarnings("unchecked") public static final Issue ISSUE = Issue.create( "ResourceName", //Issue id 这个就是平时利用tools 方式,屏蔽问题时使用的属性 "Resource with Wrong Prefix", //这里对应AS Inspection 中的具体条目 //这里展示的是描述信息 "In Gradle projects you can specify a resource prefix that all resources " + "in the project must conform to. This makes it easier to ensure that you don't " + "accidentally combine resources from different libraries, since they all end " + "up in the same shared app namespace.", Category.CORRECTNESS, 8, Severity.FATAL, new Implementation( ResourcePrefixDetector.class, EnumSet.of(Scope.RESOURCE_FILE, Scope.BINARY_RESOURCE_FILE), Scope.RESOURCE_FILE_SCOPE, Scope.BINARY_RESOURCE_FILE_SCOPE)); private String mPrefix; //gradle 中设置的前缀resourcePrefix //从Gradle 配置中读取配置的resourcePrefix的方法 @Nullable private static String computeResourcePrefix(@NonNull Project project) { if (project.isGradleProject()) { return LintUtils.computeResourcePrefix(project.getGradleProjectModel()); } return null; } /** * 筛选可以过滤name 的根元素,是否存在值 * TAG_RESOURCES == resources * TAG_DECLARE_STYLEABLE == declare-styleable */ @Override public Collection<String> getApplicableElements() { return Arrays.asList(TAG_RESOURCES, TAG_DECLARE_STYLEABLE); } /** * 针对xml文件进行扫描的规则 * 检测可识别 */ @Override public void beforeCheckFile(@NonNull Context context) { //仅检测XML文件 if (mPrefix != null && context instanceof XmlContext) { XmlContext xmlContext = (XmlContext) context; ResourceFolderType folderType = xmlContext.getResourceFolderType(); //可以识别的folderType(文件夹类型)中,values文件夹下不做文件名前缀的检测 if (folderType != null && folderType != ResourceFolderType.VALUES) { String name = LintUtils.getBaseName(context.file.getName()); if (!name.startsWith(mPrefix)) { // Attempt to report the error on the root tag of the associated // document to make suppressing the error with a tools:suppress // attribute etc possible if (xmlContext.document != null) { Element root = xmlContext.document.getDocumentElement(); if (root != null) { //存在根元素,错误报告在根元素上 xmlContext.report(ISSUE, root, xmlContext.getLocation(root),getErrorMessage(name)); return; } } //不存在跟元素,错误直接报告在文件上 context.report(ISSUE, Location.create(context.file),getErrorMessage(name)); } } } } /** * 针对values目录下的xml文件进行扫描的规则 * 检测 包含name 元素的的值,是否带有前缀 * ATTR_NAME == name */ @Override public void visitElement(@NonNull XmlContext context, @NonNull Element element) { if (mPrefix == null || context.getResourceFolderType() != ResourceFolderType.VALUES) { return; } for (Element item : LintUtils.getChildren(element)) { Attr nameAttribute = item.getAttributeNode(ATTR_NAME); if (nameAttribute != null) { String name = nameAttribute.getValue(); if (!name.startsWith(mPrefix)) { String message = getErrorMessage(name); //错误直接报告在 name 字段上 context.report(ISSUE, nameAttribute, context.getLocation(nameAttribute), message); } } } } /** * 针对二进制文件的扫描 * 检测二进制文件名与要求前缀是否匹配 */ @Override public void checkBinaryResource(@NonNull ResourceContext context) { //res 目录下,可识别的文件夹类型中, 除了values类型, 剩余文件的前缀检测 if (mPrefix != null) { ResourceFolderType folderType = context.getResourceFolderType(); if (folderType != null && folderType != ResourceFolderType.VALUES) { String name = LintUtils.getBaseName(context.file.getName()); if (!name.startsWith(mPrefix)) { Location location = Location.create(context.file); //错误直接报告在文件上 context.report(ISSUE, location, getErrorMessage(name)); } } } } //展示错误信息,就是平时看见的AS上实时展示的错误提示 private String getErrorMessage(String name) { assert mPrefix != null && !name.startsWith(mPrefix); return String.format("Resource named '`%1$s`' does not start " + "with the project's resource prefix '`%2$s`'; rename to '`%3$s`' ?", name, mPrefix, LintUtils.computeResourceName(mPrefix, name)); } }