resourcePrefix深入解析
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));
}
}