作数据打点的时候,若是打点字段重复了怎么办?java
例如:程序员
申明统计打点字段.swift
/** * 页面1打开次数 */
public static final int COUNT_KEY_1 = 10007;
/** * 页面2打开次数 */
public static final int COUNT_KEY_2 = 10008;
复制代码
其余团队作广告模块的同窗不知道10007和10008字段被占用了.继续申明了markdown
/** * 广告打开次数 */
public static final int COUNT_KEY_AD = 10008;
复制代码
这个时候字段10008就被污染了,广告打开次数和页面2打开次数就没法准确统计到.app
实际工做中,2个场景容易出现上诉事故.ide
指望有个工具能够检测重复定义的字段模块化
iOS同窗能够利用enum的语法特性+协议来解决.实例示例代码以下:函数
enum Model: Int: ModelProtocol {
case home = 11
case sounds = 12
public func toCode() -> Int64 {
self.rawValue
}
}
enum SleepPlan: Int64, EventCodeProtocol {
var model: Model {
return .home
}
case click = 0001
case show = 0002
public func toCode() -> Int64 {
self.rawValue
}
public func type() -> EventTypeCode {
.count
}
public func immediately() -> Bool {
true
}
}
protocol ModelProtocol {
func toCode() -> Int64
}
extension EventCodeProtocol {
var model: ModelProtocol
}
func log(code: EventCodeProtocol) {
code.model.toCode() * 1000 + code.toCode()
}
复制代码
交流了下实现原理,就是让定义重复打点的时候触发一次编译器语法错误,让编译失败,从而让开发人员知道有字段重复定义了.工具
那么Android上怎么开发,Java能够利用switch...case...语法不能有重复字段申明的特性,把上诉问题转化为下面的代码ui
那么问题是: 怎么来实现这个校验函数?,若是本身手写的话耗时,耗力,还容易写错.
比起手动去维护这个函数,APT的优点是能够自动生成从而不易出错.
参考ButterKnife、EventBus的实现后,总结APT的实现步骤大体以下:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Statistics {
Type type();
public enum Type {
String, Int
}
}
复制代码
而后在原项目引用
@Statistics(type = Statistics.Type.Int)
public class StaticDemo2 {
复制代码
这一步的目的是让APT知道须要处理哪些类.
@AutoService(Processor.class)
public class StatisticsProcessor extends AbstractProcessor {
private Filer mFilerUtils; // 文件管理工具类
private Types mTypesUtils; // 类型处理工具类
private Elements mElementsUtils; // Element处理工具类
static Set<String> typeSet = new HashSet<>();
private Map<Statistics.Type, List<String>> dataMap = new HashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFilerUtils = processingEnv.getFiler();
mTypesUtils = processingEnv.getTypeUtils();
mElementsUtils = processingEnv.getElementUtils();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {
System.out.println("start process");
if (set != null && set.size() != 0) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Statistics.class);
categories(elements);
if (dataMap.size() > 0) {
Element simpleElement = elements.iterator().next();
String code = generateCode(simpleElement, dataMap.get(Statistics.Type.String), dataMap
.get(Statistics.Type.Int));
String helperClassName = "StatisticsChecker"; // 构建要生成的帮助类的类名
try {
JavaFileObject jfo = mFilerUtils.createSourceFile(helperClassName);
Writer writer = jfo.openWriter();
writer.write(code);
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
return false;
}
private void categories(Set<? extends Element> elements) {
if (typeSet.size() == 0) {
Statistics.Type[] types = Statistics.Type.values();
for (int i = 0; i < types.length; i++) {
typeSet.add(types[i].name().toLowerCase());
}
}
for (Element element : elements) {
Statistics statistics = element.getAnnotation(Statistics.class);
Symbol.ClassSymbol cE = (Symbol.ClassSymbol) element;
String preName = cE.className();
List eleSubList = element.getEnclosedElements();
Statistics.Type type = statistics.type();
List list = dataMap.get(type);
if (list == null) {
list = new ArrayList<String>();
dataMap.put(type, list);
}
for (Object e : eleSubList) {
if (e instanceof Symbol.VarSymbol) {
Symbol.VarSymbol el = (Symbol.VarSymbol) e;
if (typeSet.contains(el.type.tsym.name.toString().toLowerCase())) {
// 其实这里就能够知道有没有重复字段了 可是为了让结果更加直观 仍是把这个代码生成出来看
list.add(preName + "." + (el).getSimpleName().toString());
}
}
}
}
}
private String generateCode(Element typeElement, List<String> listStr, List<String> listInt) {
String packageName = ((PackageElement) mElementsUtils.getPackageOf(typeElement))
.getQualifiedName().toString(); // 获取要绑定包名
String helperClassName = "StatisticsChecker"; // 要生成的帮助类的名称
StringBuilder builder = new StringBuilder();
builder.append("package ").append(packageName).append(";\n");
builder.append("\n");
builder.append("public class ").append(helperClassName);
builder.append(" {\n");
builder.append("\tvoid check() {\n");
if (listStr != null && listStr.size() > 0) {
builder.append("\t\tString countStr = null;\n");
builder.append("\t\tswitch (countStr) {\n");
for (String caseValue : listStr) {
builder.append("\t\t\t");
builder.append(String.format("case %s:\n", caseValue));
}
builder.append("\t\t}\n");
}
if (listInt != null && listInt.size() > 0) {
builder.append("\t\tint countInt = 0;\n");
builder.append("\t\tswitch (countInt) {\n");
for (String caseValue : listInt) {
builder.append("\t\t\t");
builder.append(String.format("case %s:\n", caseValue));
}
builder.append("\t\t}\n");
}
builder.append("\t}\n");
builder.append("}\n");
return builder.toString();
}
}
复制代码
这里是模板写法, 继承AbstractProcessor类实现他的process方法. 目的是在并编译的时候自动生成代码.
看ButterKnife还有一个注入的过程,就是把自动生成的代码放到原来的项目里面调用,可是咱们这里只是用来作静态检测,生成便可,不须要注入.
到此APT自动检测重复字段就实现了,可是还有几个问题没有解决: