Всем привет!
Недавно передо мной встала задача написания своих кастомных аннотации и их обработки во время компиляции. Первый вопрос, который я себе задала: с чего начать? После анализа я решила поделиться с вами ответом на этот вопрос.
Думаю, рассказывать, что такое аннотации в java и с чем их едят, не имеет смысла, так как каждому юному программисту это знакомо ( а кому не знакомо, может прочесть самостоятельно). К тому же на хабре есть интересная ознакомительная статья об этом явлении.
Но сегодня я хочу поговорить именно о кастомных аннотациях в Android-приложении, которые обрабатываются в процессе компиляции проекта вашим собственным обработчиком и о автогенерации классов на их основе. А так же, по ходу дела, расскажу вам, как быстро все настроить в IDEA (сама я пользуюсь версией 12.1, возможно в других есть отличия).
Для реализации нам понадобится 2 проекта: в первом опишем наши кастомные аннотации и их обработчик, из него сгенерируем jar файл, который подключим ко второму тестовому проекту, который и будет использовать наши аннотации.
Шаг 1
Создаем новый library проект, в котором описываем свою аннотацию. Класс с аннотацией выглядит примерно так:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface CustomAnnotation {
String className();
String value() default "Hello";
int type() default 0;
}
@Retention говорит о том, что наша аннотация будет присутствовать только в исходном коде и отброшена компилятором (а до этого момента мы её обработаем).
Шаг 2
Создаем непосредственно наш обработчик аннотаций. Он представляет собой класс, расширяющий AbstractProcessor. Ему мы говорим, что он будет обрабатывать все аннотации и указываем поддерживаемую версию исходных файлов таким образом:
@SupportedAnnotationTypes({"*"})
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class CustomProcessor extends AbstractProcessor {
}
Далее переопределяем метод process, в котором прописываем логику генерации нового класса. Мой метод выглядит так:
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element e : roundEnv.getElementsAnnotatedWith(CustomAnnotation.class)) {
CustomAnnotation ca = e.getAnnotation(CustomAnnotation.class);
String name = e.getSimpleName().toString();
char[] c = name.toCharArray();
c[0] = Character.toUpperCase(c[0]);
name = new String(name);
TypeElement clazz = (TypeElement) e.getEnclosingElement();
try {
JavaFileObject f = processingEnv.getFiler().
createSourceFile(clazz.getQualifiedName() + "Autogenerate");
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
"Creating " + f.toUri());
Writer w = f.openWriter();
try {
String pack = clazz.getQualifiedName().toString();
PrintWriter pw = new PrintWriter(w);
pw.println("package "
+ pack.substring(0, pack.lastIndexOf('.')) + ";");
pw.println("npublic class "
+ clazz.getSimpleName() + "Autogenerate {");
TypeMirror type = e.asType();
pw.println("n public " + ca.className() + " result = "" + ca.value() + "";");
pw.println(" public int type = " + ca.type() + ";");
pw.println("n protected " + clazz.getSimpleName()
+ "Autogenerate() {}");
pw.println("n /** Handle something. */");
pw.println(" protected final void handle" + name
+ "(" + ca.className() + " value" + ") {");
pw.println("n//" + e);
pw.println("//" + ca);
pw.println("n System.out.println(value);");
pw.println(" }");
pw.println("}");
pw.flush();
} finally {
w.close();
}
} catch (IOException x) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
x.toString());
}
}
return true;
}
На этом этапе можно включить фантазию и писать, что душе угодно (ну или что требуется =). После того, как вы закончили с обработчиком аннотаций и описали все свои кастомные аннотации, из этого проекта нужно сгенерировать jar файл. В Idea 12 это делается достаточно просто: Project Settings -> Artifacts -> Add -> Jar -> From modules… Далее делаем Build -> Rebuild Project и находит наш сгенерированный файл jar в Output директории проекта.
Шаг 3
Создаем тестовый проект, в котором и будем использовать наши кастомные аннотации. К проекту подключаем сгенерированный на прошлом шаге jar файл и радуемся, что наши аннотации теперь нам доступны. В любом классе прописываем нашу аннотацию, например так:
public class MyActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@CustomAnnotation(className = "String", type = 1)
public void annotatedMethod(String value) {
}
}
Помните, что мы указали нашей аннотации @Target(ElementType.METHOD)
, а это значит что мы можем прописать ее только перед методом.
Шаг 4
Теперь скажем Idea использовать наш обработчик аннотаций и магия начнет работать! Для этого идём в Settings в раздел Compiler — > Annotation Processors. Ставим галочку Enable annotation processing, в поле Processor path указываем путь к нашему сгенерированному jar файлу. Так же в окне Processor FQ name вводим полное название класса, который отвечает за обработку. В нашем случает это CustomProcessor. Заполненное окно должно выглядеть примерно так.
Шаг 5
Делаем Build -> Rebuild project и наслаждаемся результатами. В дереве проекта должна появиться папка generated, в которой будут лежать новые файлы.
Вот что получилось у меня:
public class MyActivityAutogenerate {
public String result = "Hello";
public int type = 1;
protected MyActivityAutogenerate() {}
/** Handle something. */
protected final void handleannotatedMethod(String value) {
//annotatedMethod(java.lang.String)
//@com.example.AnnotationsProcessor.CustomAnnotation(type=1, value=Hello, className=String)
System.out.println(value);
}
}
Happy codding!
Автор: Juli_Incognito