SlideShare uma empresa Scribd logo
1 de 96
Baixar para ler offline
Write code that writes code!
A beginner’s guide to annotation processing.
Obligatory Speaker Details
• Software Engineer for Bandcamp
• I’m from the US, but am living in Europe for now.
(working remotely)
• I have a dog named Watson. On weekends, we
walk across the Netherlands together.
Questions we ask ourselves in the
beginning.
• What is an annotation, and what is annotation
processing?
• Why would I want to process annotations?
• How do I make something cool? Maybe a
ButterKnife clone?
–http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html
“…an annotation is a form of syntactic
metadata…”
Annotations
–http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html
“Annotations do not directly affect program
semantics, but they do affect the way programs
are treated by tools and libraries, which can in
turn affect the semantics of the running
program.”
Annotations
Annotations
• You’ve seen them before (e.g. @Override, @Deprecated, etc.)
• They allow you to decorate code with information about the
code (ie: they are meta data)
• Kind of like comments, but they are more machine
readable than human readable.
• Annotations can be used by the JDK, third party libraries, or
custom tools.
• You can create your own annotations.
Custom annotations are
useless..
… until you use them.
Custom Annotations
• Useless
• Useless (until you actually use them…)
Custom Annotations
• Useless
• Useless
• Run-time - with reflection
• Compile-time “Annotation Processor”
• Useless (until you actually use them…)
Custom Annotations
• Useless
–Everyone
“Reflection is slow and you should never use it.”
–Smart People
“Reflection is slow and you should try to avoid
using it on the main thread.”
Annotation Processors
• Operate at build-time, rather than run-time.
• Are executed by the “annotation processing
tool” (apt)
• Must be part of a plain-old java library, without
direct dependencies on Android-specific stuff.
• Extend from
javax.annotation.processing.AbstractProcessor
Annotation Processing
List unprocessed
source files with
annotations.
Register
Annotation
Processors
Any
Processors
for them?
Run ProcessorsCompile
No* Yes
Annotation Processing
* If a processor was asked to process on a given round, it will be asked to process on
subsequent rounds, including the last round, even if there are no annotations for it to
process. 



https://docs.oracle.com/javase/8/docs/api/javax/annotation/processing/Processor.html
List unprocessed
source files with
annotations.
Register
Annotation
Processors
Any
Processors
for them?
Run ProcessorsCompile
No* Yes
Why would you want to make one?
• Boilerplate Reduction
• Reducing Boilerplate
• Reduced Boilerplate
• ….
• It’s pretty cool.
Let’s make one.
“Soup Ladle”
• Wanted something that sounded like Butter
Knife, but was a different utensil.
• I like Soup.
• Ladles are big spoons.
• Big spoon = more soup in my face at once.
Soup Ladle Goals
• Allow for view binding with an annotation:

@Bind(R.id.some_id) View fieldName;
• Perform the binding easily using a one liner in
onCreate:

SoupLadle.bind(this);
• That’s it.. we are reinventing the wheel for
learning’s sake and don’t need to go all in.
Approach
1. Define the @Bind annotation.
2. Extend AbstractProcessor to create our
annotation processor for @Bind.
3. Within our processor: scan for all fields with
@Bind, keeping track of their parent classes.
4. Generate SoupLadle.java with .bind methods
for each parent class containing bound fields.
Approach
1. Define the @Bind annotation.
2. Extend AbstractProcessor to create our
annotation processor for @Bind.
3. Within our processor: scan for all fields with
@Bind, keeping track of their parent classes.
4. Generate SoupLadle.java with .bind methods
for each parent class containing bound fields.
Define @Bind
@Target(ElementType.FIELD)

@Retention(RetentionPolicy.SOURCE)

public @interface Bind {

int value();

}
Define @Bind
@Target(ElementType.FIELD)

@Retention(RetentionPolicy.SOURCE)

public @interface Bind {

int value();

}
Define @Bind
@Target(ElementType.FIELD)

@Retention(RetentionPolicy.SOURCE)

public @interface Bind {

int value();

}
Define @Bind
@Target(ElementType.FIELD)

@Retention(RetentionPolicy.SOURCE)

public @interface Bind {

int value();

}
Define @Bind
@Target(ElementType.FIELD)

@Retention(RetentionPolicy.SOURCE)

public @interface Bind {

int value();

}
Approach
1. Define the @Bind annotation.
2. Extend AbstractProcessor to create our
annotation processor for @Bind.
3. Within our processor: scan for all fields with
@Bind, keeping track of their parent classes.
4. Generate SoupLadle.java with .bind methods
for each parent class containing bound fields.
Extending AbstractProcessor
public class AnnotationProcessor extends AbstractProcessor {

private Filer mFiler;



@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

mFiler = processingEnv.getFiler();

}



@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.latestSupported();

}



@Override

public Set<String> getSupportedAnnotationTypes() {

HashSet<String> result = new HashSet<>();

result.add(Bind.class.getCanonicalName());

return result;

}



@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// logic goes here
// as does the actual code generation
//
// we’ll get to this stuff in a little bit
}
}
Extending AbstractProcessor
public class AnnotationProcessor extends AbstractProcessor {

private Filer mFiler;



@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

mFiler = processingEnv.getFiler();

}



@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.latestSupported();

}



@Override

public Set<String> getSupportedAnnotationTypes() {

HashSet<String> result = new HashSet<>();

result.add(Bind.class.getCanonicalName());

return result;

}



@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// logic goes here
// as does the actual code generation
//
// we’ll get to this stuff in a little bit
}
}
Extending AbstractProcessor
public class AnnotationProcessor extends AbstractProcessor {

private Filer mFiler;



@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

mFiler = processingEnv.getFiler();

}



@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.latestSupported();

}



@Override

public Set<String> getSupportedAnnotationTypes() {

HashSet<String> result = new HashSet<>();

result.add(Bind.class.getCanonicalName());

return result;

}



@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// logic goes here
// as does the actual code generation
//
// we’ll get to this stuff in a little bit
}
}
Extending AbstractProcessor
public class AnnotationProcessor extends AbstractProcessor {

private Filer mFiler;



@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

mFiler = processingEnv.getFiler();

}



@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.latestSupported();

}



@Override

public Set<String> getSupportedAnnotationTypes() {

HashSet<String> result = new HashSet<>();

result.add(Bind.class.getCanonicalName());

return result;

}



@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// logic goes here
// as does the actual code generation
//
// we’ll get to this stuff in a little bit
}
}
Extending AbstractProcessor
public class AnnotationProcessor extends AbstractProcessor {

private Filer mFiler;



@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

mFiler = processingEnv.getFiler();

}



@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.latestSupported();

}



@Override

public Set<String> getSupportedAnnotationTypes() {

HashSet<String> result = new HashSet<>();

result.add(Bind.class.getCanonicalName());

return result;

}



@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// logic goes here
// as does the actual code generation
//
// we’ll get to this stuff in a little bit
}
}
Extending AbstractProcessor
public class AnnotationProcessor extends AbstractProcessor {

private Filer mFiler;



@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

mFiler = processingEnv.getFiler();

}



@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.latestSupported();

}



@Override

public Set<String> getSupportedAnnotationTypes() {

HashSet<String> result = new HashSet<>();

result.add(Bind.class.getCanonicalName());

return result;

}



@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// logic goes here
// as does the actual code generation
//
// we’ll get to this stuff in a little bit
}
}
Extending AbstractProcessor
public class AnnotationProcessor extends AbstractProcessor {

private Filer mFiler;



@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

mFiler = processingEnv.getFiler();

}



@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.latestSupported();

}



@Override

public Set<String> getSupportedAnnotationTypes() {

HashSet<String> result = new HashSet<>();

result.add(Bind.class.getCanonicalName());

return result;

}



@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// logic goes here
// as does the actual code generation
//
// we’ll get to this stuff in a little bit
}
}
Approach
1. Define the @Bind annotation.
2. Extend AbstractProcessor to create our
annotation processor for @Bind.
3. Within our processor: scan for all fields with
@Bind, keeping track of their parent classes.
4. Generate SoupLadle.java with .bind methods
for each parent class containing bound fields.
Processing…
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

if (annotations.isEmpty()) {

return true;

}



Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>();

for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) {

VariableElement variable = (VariableElement) e;

TypeElement parent = (TypeElement) variable.getEnclosingElement();



List<VariableElement> members;

if (bindingClasses.containsKey(parentClass)) {

members = bindingClasses.get(parentClass);

} else {

members = new ArrayList<>();

bindingClasses.put(parentClass, members);

}

members.add(variable);

}
// .. generate code ..
}
Processing…
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

if (annotations.isEmpty()) {

return true;

}



Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>();

for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) {

VariableElement variable = (VariableElement) e;

TypeElement parent = (TypeElement) variable.getEnclosingElement();



List<VariableElement> members;

if (bindingClasses.containsKey(parentClass)) {

members = bindingClasses.get(parentClass);

} else {

members = new ArrayList<>();

bindingClasses.put(parentClass, members);

}

members.add(variable);

}
// .. generate code ..
}
Processing…
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

if (annotations.isEmpty()) {

return true;

}



Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>();

for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) {

VariableElement variable = (VariableElement) e;

TypeElement parent = (TypeElement) variable.getEnclosingElement();



List<VariableElement> members;

if (bindingClasses.containsKey(parentClass)) {

members = bindingClasses.get(parentClass);

} else {

members = new ArrayList<>();

bindingClasses.put(parentClass, members);

}

members.add(variable);

}
// .. generate code ..
}
Processing…
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

if (annotations.isEmpty()) {

return true;

}



Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>();

for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) {

VariableElement variable = (VariableElement) e;

TypeElement parent = (TypeElement) variable.getEnclosingElement();



List<VariableElement> members;

if (bindingClasses.containsKey(parentClass)) {

members = bindingClasses.get(parentClass);

} else {

members = new ArrayList<>();

bindingClasses.put(parentClass, members);

}

members.add(variable);

}
// .. generate code ..
}
Approach
1. Define the @Bind annotation.
2. Extend AbstractProcessor to create our
annotation processor for @Bind.
3. Within our processor: scan for all fields with
@Bind, keeping track of their parent classes.
4. Generate SoupLadle.java with .bind methods
for each parent class containing bound fields.
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



} catch (IOException e) {

throw new RuntimeException(e);

}
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



} catch (IOException e) {

throw new RuntimeException(e);

}
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



} catch (IOException e) {

throw new RuntimeException(e);

}
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



} catch (IOException e) {

throw new RuntimeException(e);

}
}
😱
There has to be a better way!?
There is a better way.
Introducing: JavaPoet
Introducing: JavaPoet
By Square (Of Course)
JavaPoet
• Builder-pattern approach to programmatically
defining a class and its fields/methods.
• Automatically manages the classes needed for
import.
• When you’re ready, it will write clean & readable
Java source to an OutputStream/Writer.
JavaPoet - Hello World
TypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld")

.addModifiers(Modifier.PUBLIC)

.addMethod(MethodSpec.methodBuilder("main")

.addModifiers(Modifier.PUBLIC, Modifier.STATIC)

.returns(void.class)

.addParameter(String[].class, "args")

.addStatement("System.out.println($S + args[0])", "Hello: ")

.build());

JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out);
package jwf.soupladle;



import java.lang.String;



public class HelloWorld {

public static void main(String[] args) {

System.out.println("Hello: " + args[0]);

}

}

JavaPoet - Hello World
TypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld")

.addModifiers(Modifier.PUBLIC)

.addMethod(MethodSpec.methodBuilder("main")

.addModifiers(Modifier.PUBLIC, Modifier.STATIC)

.returns(void.class)

.addParameter(String[].class, "args")

.addStatement("System.out.println($S + args[0])", "Hello: ")

.build());

JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out);
package jwf.soupladle;



import java.lang.String;



public class HelloWorld {

public static void main(String[] args) {

System.out.println("Hello: " + args[0]);

}

}

JavaPoet - Hello World
TypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld")

.addModifiers(Modifier.PUBLIC)

.addMethod(MethodSpec.methodBuilder("main")

.addModifiers(Modifier.PUBLIC, Modifier.STATIC)

.returns(void.class)

.addParameter(String[].class, "args")

.addStatement("System.out.println($S + args[0])", "Hello: ")

.build());

JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out);
package jwf.soupladle;



import java.lang.String;



public class HelloWorld {

public static void main(String[] args) {

System.out.println("Hello: " + args[0]);

}

}

JavaPoet - Hello World
TypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld")

.addModifiers(Modifier.PUBLIC)

.addMethod(MethodSpec.methodBuilder("main")

.addModifiers(Modifier.PUBLIC, Modifier.STATIC)

.returns(void.class)

.addParameter(String[].class, "args")

.addStatement("System.out.println($S + args[0])", "Hello: ")

.build());

JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out);
package jwf.soupladle;



import java.lang.String;



public class HelloWorld {

public static void main(String[] args) {

System.out.println("Hello: " + args[0]);

}

}

JavaPoet - Hello World
👏
TypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld")

.addModifiers(Modifier.PUBLIC)

.addMethod(MethodSpec.methodBuilder("main")

.addModifiers(Modifier.PUBLIC, Modifier.STATIC)

.returns(void.class)

.addParameter(String[].class, "args")

.addStatement("System.out.println($S + args[0])", "Hello: ")

.build());

JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out);
package jwf.soupladle;



import java.lang.String;



public class HelloWorld {

public static void main(String[] args) {

System.out.println("Hello: " + args[0]);

}

}

Approach
1. Define the @Bind annotation.
2. Extend AbstractProcessor to create our
annotation processor for @Bind.
3. Within our processor: scan for all fields with
@Bind, keeping track of their parent classes.
4. Generate SoupLadle.java with .bind methods
for each parent class containing bound fields.
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
"target.$L = ($T) target.findViewById($L)"
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
"target.$L = ($T) target.findViewById($L)"
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
"target.$L = ($T) target.findViewById($L)"
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
"target.$L = ($T) target.findViewById($L)"
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
👀
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

// .. process annotations ..
try {

JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");



TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL);



for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {

TypeName typeParameter = ClassName.get(binding.getKey());



MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)

.addParameter(typeParameter, "target");



List<VariableElement> members = binding.getValue();

for (VariableElement member : members) {

Bind annotation = member.getAnnotation(Bind.class);

TypeName castClass = ClassName.get(member.asType());

bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)",
member.getSimpleName().toString(),
castClass,
annotation.value());

}



soupLadleBuilder.addMethod(bindingBuilder.build());

}



AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType").build();
soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation);


JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();

Writer out = jfo.openWriter();

file.writeTo(out);

out.flush();

out.close();

} catch (IOException e) {

throw new RuntimeException(e);

}
return true;
}
We’ve got an annotation
processor now!
How do we tell the build
process about it?
Project/Module Config
• The annotation processor and binding
annotation class need to live in a “regular” java
module.
• Add the android-apt gradle plugin to your root
build.gradle.
• Add dependency records to your app’s
build.gradle.
Project/Module Config
• The annotation processor and binding
annotation class need to live in a “regular” java
module.
• Add the android-apt gradle plugin to your root
build.gradle.
• Add dependency records to your app’s
build.gradle.
SoupLadle Module
SoupLadle Module


apply plugin: 'java'



dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])

compile 'com.squareup:javapoet:1.7.0'

}
SoupLadle Module
jwf.soupladle.AnnotationProcessor
SoupLadle Module
include ':app', ':library'
Project/Module Config
• The annotation processor and binding
annotation class need to live in a “regular” java
module.
• Add the android-apt gradle plugin to your root
build.gradle.
• Add dependency records to your app’s
build.gradle.
Project build.gradle
buildscript {

repositories {

jcenter()

}

dependencies {

classpath 'com.android.tools.build:gradle:2.1.3'

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

}

}



allprojects {

repositories {

jcenter()

}

}



task clean(type: Delete) {

delete rootProject.buildDir

}
Project build.gradle
buildscript {

repositories {

jcenter()

}

dependencies {

classpath 'com.android.tools.build:gradle:2.1.3'

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

}

}



allprojects {

repositories {

jcenter()

}

}



task clean(type: Delete) {

delete rootProject.buildDir

}
Project/Module Config
• The annotation processor and binding
annotation class need to live in a “regular” java
module.
• Add the android-apt gradle plugin to your root
build.gradle.
• Add dependency records to your app’s
build.gradle.
App build.gradle
apply plugin: 'com.android.application'

apply plugin: 'com.neenbedankt.android-apt'



android {

compileSdkVersion 24

buildToolsVersion "24.0.2"



defaultConfig {

applicationId "jwf.soupladle.example"

minSdkVersion 16

targetSdkVersion 24

versionCode 1

versionName "1.0"

}

buildTypes {

// .. your build types ..

}

}



dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])

compile 'com.android.support:appcompat-v7:24.1.1'



apt project(':library')

provided project(':library')

}
App build.gradle
apply plugin: 'com.android.application'

apply plugin: 'com.neenbedankt.android-apt'



android {

compileSdkVersion 24

buildToolsVersion "24.0.2"



defaultConfig {

applicationId "jwf.soupladle.example"

minSdkVersion 16

targetSdkVersion 24

versionCode 1

versionName "1.0"

}

buildTypes {

// .. your build types ..

}

}



dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])

compile 'com.android.support:appcompat-v7:24.1.1'



apt project(':library')

provided project(':library')

}
App build.gradle
apply plugin: 'com.android.application'

apply plugin: 'com.neenbedankt.android-apt'



android {

compileSdkVersion 24

buildToolsVersion "24.0.2"



defaultConfig {

applicationId "jwf.soupladle.example"

minSdkVersion 16

targetSdkVersion 24

versionCode 1

versionName "1.0"

}

buildTypes {

// .. your build types ..

}

}



dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])

compile 'com.android.support:appcompat-v7:24.1.1'



apt project(':library')

provided project(':library')

}
Let’s try using it!
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:paddingBottom="@dimen/activity_vertical_margin"

android:paddingLeft="@dimen/activity_horizontal_margin"

android:paddingRight="@dimen/activity_horizontal_margin"

android:paddingTop="@dimen/activity_vertical_margin"

tools:context="jwf.soupladle.example.MainActivity">



<TextView

android:id="@+id/hello_world"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Hello World!"/>

</RelativeLayout>

MainActivity.java
package jwf.soupladle.example;



import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.widget.TextView;



import jwf.soupladle.Bind;



public class MainActivity extends AppCompatActivity {

@Bind(R.id.hello_world)

public TextView textView;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

}
MainActivity.java
package jwf.soupladle.example;



import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.widget.TextView;



import jwf.soupladle.Bind;



public class MainActivity extends AppCompatActivity {

@Bind(R.id.hello_world)

public TextView textView;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

}
rebuild project…
A wild SoupLadle.java Appears!
package jwf.soupladle;



import android.widget.TextView;

import java.lang.SuppressWarnings;

import jwf.soupladle.example.MainActivity;



@SuppressWarnings("ResourceType")

public final class SoupLadle {

public static final void bind(MainActivity target) {

target.textView = (TextView) target.findViewById(2131427412);

}

}

MainActivity.java
package jwf.soupladle.example;



import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.widget.TextView;



import jwf.soupladle.Bind;

import jwf.soupladle.SoupLadle;



public class MainActivity extends AppCompatActivity {

@Bind(R.id.hello_world)

public TextView textView;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

SoupLadle.bind(this);

textView.setText("The binding worked!");

}

}
Thank you! Questions?
Twitter: @jasonwyatt
github.com/jasonwyatt
bandcamp.com/jasonwyatt
Source Code available at:
github.com/jasonwyatt/Soup-Ladle

Mais conteúdo relacionado

Mais procurados

Memory management in python
Memory management in pythonMemory management in python
Memory management in pythonGaurav Aggarwal
 
Learn To Test Like A Grumpy Programmer - 3 hour workshop
Learn To Test Like A Grumpy Programmer - 3 hour workshopLearn To Test Like A Grumpy Programmer - 3 hour workshop
Learn To Test Like A Grumpy Programmer - 3 hour workshopchartjes
 
Javaslang Talk @ Javaland 2017
Javaslang Talk @ Javaland 2017Javaslang Talk @ Javaland 2017
Javaslang Talk @ Javaland 2017David Schmitz
 
Apache Ant
Apache AntApache Ant
Apache AntAli Bahu
 
Test in action – week 1
Test in action – week 1Test in action – week 1
Test in action – week 1Yi-Huan Chan
 
Java 8 presentation
Java 8 presentationJava 8 presentation
Java 8 presentationVan Huong
 
Non blocking programming and waiting
Non blocking programming and waitingNon blocking programming and waiting
Non blocking programming and waitingRoman Elizarov
 
Using spl tools in your code
Using spl tools in your codeUsing spl tools in your code
Using spl tools in your codeElizabeth Smith
 
Test in action week 4
Test in action   week 4Test in action   week 4
Test in action week 4Yi-Huan Chan
 
Why GC is eating all my CPU?
Why GC is eating all my CPU?Why GC is eating all my CPU?
Why GC is eating all my CPU?Roman Elizarov
 
Java Serialization Facts and Fallacies
Java Serialization Facts and FallaciesJava Serialization Facts and Fallacies
Java Serialization Facts and FallaciesRoman Elizarov
 
Con-FESS 2015 - Having Fun With Javassist
Con-FESS 2015 - Having Fun With JavassistCon-FESS 2015 - Having Fun With Javassist
Con-FESS 2015 - Having Fun With JavassistAnton Arhipov
 
Java 8 Lambda Expressions & Streams
Java 8 Lambda Expressions & StreamsJava 8 Lambda Expressions & Streams
Java 8 Lambda Expressions & StreamsNewCircle Training
 

Mais procurados (20)

Javaslang @ Devoxx
Javaslang @ DevoxxJavaslang @ Devoxx
Javaslang @ Devoxx
 
Memory management in python
Memory management in pythonMemory management in python
Memory management in python
 
Learn To Test Like A Grumpy Programmer - 3 hour workshop
Learn To Test Like A Grumpy Programmer - 3 hour workshopLearn To Test Like A Grumpy Programmer - 3 hour workshop
Learn To Test Like A Grumpy Programmer - 3 hour workshop
 
Javaslang Talk @ Javaland 2017
Javaslang Talk @ Javaland 2017Javaslang Talk @ Javaland 2017
Javaslang Talk @ Javaland 2017
 
Java 8 Feature Preview
Java 8 Feature PreviewJava 8 Feature Preview
Java 8 Feature Preview
 
Apache Ant
Apache AntApache Ant
Apache Ant
 
Java 8 streams
Java 8 streamsJava 8 streams
Java 8 streams
 
Streams in Java 8
Streams in Java 8Streams in Java 8
Streams in Java 8
 
Spring boot
Spring bootSpring boot
Spring boot
 
Java8
Java8Java8
Java8
 
Test in action – week 1
Test in action – week 1Test in action – week 1
Test in action – week 1
 
Java 8 presentation
Java 8 presentationJava 8 presentation
Java 8 presentation
 
Non blocking programming and waiting
Non blocking programming and waitingNon blocking programming and waiting
Non blocking programming and waiting
 
Using spl tools in your code
Using spl tools in your codeUsing spl tools in your code
Using spl tools in your code
 
Java 8 features
Java 8 featuresJava 8 features
Java 8 features
 
Test in action week 4
Test in action   week 4Test in action   week 4
Test in action week 4
 
Why GC is eating all my CPU?
Why GC is eating all my CPU?Why GC is eating all my CPU?
Why GC is eating all my CPU?
 
Java Serialization Facts and Fallacies
Java Serialization Facts and FallaciesJava Serialization Facts and Fallacies
Java Serialization Facts and Fallacies
 
Con-FESS 2015 - Having Fun With Javassist
Con-FESS 2015 - Having Fun With JavassistCon-FESS 2015 - Having Fun With Javassist
Con-FESS 2015 - Having Fun With Javassist
 
Java 8 Lambda Expressions & Streams
Java 8 Lambda Expressions & StreamsJava 8 Lambda Expressions & Streams
Java 8 Lambda Expressions & Streams
 

Semelhante a Beginner's guide to annotation processing

JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...
JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...
JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...Jorge Hidalgo
 
Code transformation With Spoon
Code transformation With SpoonCode transformation With Spoon
Code transformation With SpoonGérard Paligot
 
How to Reverse Engineer Web Applications
How to Reverse Engineer Web ApplicationsHow to Reverse Engineer Web Applications
How to Reverse Engineer Web ApplicationsJarrod Overson
 
JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...
JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...
JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...Jorge Hidalgo
 
33rd degree talk: open and automatic coding conventions with walkmod
33rd degree talk: open and automatic coding conventions with walkmod33rd degree talk: open and automatic coding conventions with walkmod
33rd degree talk: open and automatic coding conventions with walkmodwalkmod
 
Spring MVC framework
Spring MVC frameworkSpring MVC framework
Spring MVC frameworkMohit Gupta
 
Free The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own DomainFree The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own DomainKen Collins
 
Refactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartRefactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartGabriele Lana
 
Javascript classes and scoping
Javascript classes and scopingJavascript classes and scoping
Javascript classes and scopingPatrick Sheridan
 
Cross-Platform Native Mobile Development with Eclipse
Cross-Platform Native Mobile Development with EclipseCross-Platform Native Mobile Development with Eclipse
Cross-Platform Native Mobile Development with EclipsePeter Friese
 
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go Wrong
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go WrongJDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go Wrong
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go WrongPROIDEA
 
walkmod - JUG talk
walkmod - JUG talkwalkmod - JUG talk
walkmod - JUG talkwalkmod
 
Ejb3 Struts Tutorial En
Ejb3 Struts Tutorial EnEjb3 Struts Tutorial En
Ejb3 Struts Tutorial EnAnkur Dongre
 
Ejb3 Struts Tutorial En
Ejb3 Struts Tutorial EnEjb3 Struts Tutorial En
Ejb3 Struts Tutorial EnAnkur Dongre
 
Intro To Spring Python
Intro To Spring PythonIntro To Spring Python
Intro To Spring Pythongturnquist
 
Infinum Android Talks #02 - How to write an annotation processor in Android
Infinum Android Talks #02 - How to write an annotation processor in AndroidInfinum Android Talks #02 - How to write an annotation processor in Android
Infinum Android Talks #02 - How to write an annotation processor in AndroidInfinum
 

Semelhante a Beginner's guide to annotation processing (20)

JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...
JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...
JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...
 
Code transformation With Spoon
Code transformation With SpoonCode transformation With Spoon
Code transformation With Spoon
 
How to Reverse Engineer Web Applications
How to Reverse Engineer Web ApplicationsHow to Reverse Engineer Web Applications
How to Reverse Engineer Web Applications
 
JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...
JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...
JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...
 
33rd degree talk: open and automatic coding conventions with walkmod
33rd degree talk: open and automatic coding conventions with walkmod33rd degree talk: open and automatic coding conventions with walkmod
33rd degree talk: open and automatic coding conventions with walkmod
 
Spring MVC framework
Spring MVC frameworkSpring MVC framework
Spring MVC framework
 
Java Annotations
Java AnnotationsJava Annotations
Java Annotations
 
Annotation processing
Annotation processingAnnotation processing
Annotation processing
 
Free The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own DomainFree The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own Domain
 
Refactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartRefactoring In Tdd The Missing Part
Refactoring In Tdd The Missing Part
 
Javascript classes and scoping
Javascript classes and scopingJavascript classes and scoping
Javascript classes and scoping
 
Java Basics
Java BasicsJava Basics
Java Basics
 
Cross-Platform Native Mobile Development with Eclipse
Cross-Platform Native Mobile Development with EclipseCross-Platform Native Mobile Development with Eclipse
Cross-Platform Native Mobile Development with Eclipse
 
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go Wrong
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go WrongJDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go Wrong
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go Wrong
 
walkmod - JUG talk
walkmod - JUG talkwalkmod - JUG talk
walkmod - JUG talk
 
Ejb3 Struts Tutorial En
Ejb3 Struts Tutorial EnEjb3 Struts Tutorial En
Ejb3 Struts Tutorial En
 
Ejb3 Struts Tutorial En
Ejb3 Struts Tutorial EnEjb3 Struts Tutorial En
Ejb3 Struts Tutorial En
 
Intro To Spring Python
Intro To Spring PythonIntro To Spring Python
Intro To Spring Python
 
Infinum Android Talks #02 - How to write an annotation processor in Android
Infinum Android Talks #02 - How to write an annotation processor in AndroidInfinum Android Talks #02 - How to write an annotation processor in Android
Infinum Android Talks #02 - How to write an annotation processor in Android
 
Best practices tekx
Best practices tekxBest practices tekx
Best practices tekx
 

Último

GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsRoshan Dwivedi
 

Último (20)

GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
 

Beginner's guide to annotation processing

  • 1. Write code that writes code! A beginner’s guide to annotation processing.
  • 2. Obligatory Speaker Details • Software Engineer for Bandcamp • I’m from the US, but am living in Europe for now. (working remotely) • I have a dog named Watson. On weekends, we walk across the Netherlands together.
  • 3. Questions we ask ourselves in the beginning. • What is an annotation, and what is annotation processing? • Why would I want to process annotations? • How do I make something cool? Maybe a ButterKnife clone?
  • 5. –http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html “Annotations do not directly affect program semantics, but they do affect the way programs are treated by tools and libraries, which can in turn affect the semantics of the running program.” Annotations
  • 6. Annotations • You’ve seen them before (e.g. @Override, @Deprecated, etc.) • They allow you to decorate code with information about the code (ie: they are meta data) • Kind of like comments, but they are more machine readable than human readable. • Annotations can be used by the JDK, third party libraries, or custom tools. • You can create your own annotations.
  • 9. • Useless (until you actually use them…) Custom Annotations • Useless
  • 10. • Useless • Run-time - with reflection • Compile-time “Annotation Processor” • Useless (until you actually use them…) Custom Annotations • Useless
  • 11. –Everyone “Reflection is slow and you should never use it.”
  • 12. –Smart People “Reflection is slow and you should try to avoid using it on the main thread.”
  • 13. Annotation Processors • Operate at build-time, rather than run-time. • Are executed by the “annotation processing tool” (apt) • Must be part of a plain-old java library, without direct dependencies on Android-specific stuff. • Extend from javax.annotation.processing.AbstractProcessor
  • 14. Annotation Processing List unprocessed source files with annotations. Register Annotation Processors Any Processors for them? Run ProcessorsCompile No* Yes
  • 15. Annotation Processing * If a processor was asked to process on a given round, it will be asked to process on subsequent rounds, including the last round, even if there are no annotations for it to process. 
 
 https://docs.oracle.com/javase/8/docs/api/javax/annotation/processing/Processor.html List unprocessed source files with annotations. Register Annotation Processors Any Processors for them? Run ProcessorsCompile No* Yes
  • 16. Why would you want to make one? • Boilerplate Reduction • Reducing Boilerplate • Reduced Boilerplate • …. • It’s pretty cool.
  • 18. “Soup Ladle” • Wanted something that sounded like Butter Knife, but was a different utensil. • I like Soup. • Ladles are big spoons. • Big spoon = more soup in my face at once.
  • 19. Soup Ladle Goals • Allow for view binding with an annotation:
 @Bind(R.id.some_id) View fieldName; • Perform the binding easily using a one liner in onCreate:
 SoupLadle.bind(this); • That’s it.. we are reinventing the wheel for learning’s sake and don’t need to go all in.
  • 20. Approach 1. Define the @Bind annotation. 2. Extend AbstractProcessor to create our annotation processor for @Bind. 3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes. 4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.
  • 21. Approach 1. Define the @Bind annotation. 2. Extend AbstractProcessor to create our annotation processor for @Bind. 3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes. 4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.
  • 27. Approach 1. Define the @Bind annotation. 2. Extend AbstractProcessor to create our annotation processor for @Bind. 3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes. 4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.
  • 28. Extending AbstractProcessor public class AnnotationProcessor extends AbstractProcessor {
 private Filer mFiler;
 
 @Override
 public synchronized void init(ProcessingEnvironment processingEnv) {
 super.init(processingEnv);
 mFiler = processingEnv.getFiler();
 }
 
 @Override
 public SourceVersion getSupportedSourceVersion() {
 return SourceVersion.latestSupported();
 }
 
 @Override
 public Set<String> getSupportedAnnotationTypes() {
 HashSet<String> result = new HashSet<>();
 result.add(Bind.class.getCanonicalName());
 return result;
 }
 
 @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }
  • 29. Extending AbstractProcessor public class AnnotationProcessor extends AbstractProcessor {
 private Filer mFiler;
 
 @Override
 public synchronized void init(ProcessingEnvironment processingEnv) {
 super.init(processingEnv);
 mFiler = processingEnv.getFiler();
 }
 
 @Override
 public SourceVersion getSupportedSourceVersion() {
 return SourceVersion.latestSupported();
 }
 
 @Override
 public Set<String> getSupportedAnnotationTypes() {
 HashSet<String> result = new HashSet<>();
 result.add(Bind.class.getCanonicalName());
 return result;
 }
 
 @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }
  • 30. Extending AbstractProcessor public class AnnotationProcessor extends AbstractProcessor {
 private Filer mFiler;
 
 @Override
 public synchronized void init(ProcessingEnvironment processingEnv) {
 super.init(processingEnv);
 mFiler = processingEnv.getFiler();
 }
 
 @Override
 public SourceVersion getSupportedSourceVersion() {
 return SourceVersion.latestSupported();
 }
 
 @Override
 public Set<String> getSupportedAnnotationTypes() {
 HashSet<String> result = new HashSet<>();
 result.add(Bind.class.getCanonicalName());
 return result;
 }
 
 @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }
  • 31. Extending AbstractProcessor public class AnnotationProcessor extends AbstractProcessor {
 private Filer mFiler;
 
 @Override
 public synchronized void init(ProcessingEnvironment processingEnv) {
 super.init(processingEnv);
 mFiler = processingEnv.getFiler();
 }
 
 @Override
 public SourceVersion getSupportedSourceVersion() {
 return SourceVersion.latestSupported();
 }
 
 @Override
 public Set<String> getSupportedAnnotationTypes() {
 HashSet<String> result = new HashSet<>();
 result.add(Bind.class.getCanonicalName());
 return result;
 }
 
 @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }
  • 32. Extending AbstractProcessor public class AnnotationProcessor extends AbstractProcessor {
 private Filer mFiler;
 
 @Override
 public synchronized void init(ProcessingEnvironment processingEnv) {
 super.init(processingEnv);
 mFiler = processingEnv.getFiler();
 }
 
 @Override
 public SourceVersion getSupportedSourceVersion() {
 return SourceVersion.latestSupported();
 }
 
 @Override
 public Set<String> getSupportedAnnotationTypes() {
 HashSet<String> result = new HashSet<>();
 result.add(Bind.class.getCanonicalName());
 return result;
 }
 
 @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }
  • 33. Extending AbstractProcessor public class AnnotationProcessor extends AbstractProcessor {
 private Filer mFiler;
 
 @Override
 public synchronized void init(ProcessingEnvironment processingEnv) {
 super.init(processingEnv);
 mFiler = processingEnv.getFiler();
 }
 
 @Override
 public SourceVersion getSupportedSourceVersion() {
 return SourceVersion.latestSupported();
 }
 
 @Override
 public Set<String> getSupportedAnnotationTypes() {
 HashSet<String> result = new HashSet<>();
 result.add(Bind.class.getCanonicalName());
 return result;
 }
 
 @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }
  • 34. Extending AbstractProcessor public class AnnotationProcessor extends AbstractProcessor {
 private Filer mFiler;
 
 @Override
 public synchronized void init(ProcessingEnvironment processingEnv) {
 super.init(processingEnv);
 mFiler = processingEnv.getFiler();
 }
 
 @Override
 public SourceVersion getSupportedSourceVersion() {
 return SourceVersion.latestSupported();
 }
 
 @Override
 public Set<String> getSupportedAnnotationTypes() {
 HashSet<String> result = new HashSet<>();
 result.add(Bind.class.getCanonicalName());
 return result;
 }
 
 @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }
  • 35. Approach 1. Define the @Bind annotation. 2. Extend AbstractProcessor to create our annotation processor for @Bind. 3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes. 4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.
  • 36. Processing… @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 if (annotations.isEmpty()) {
 return true;
 }
 
 Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>();
 for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) {
 VariableElement variable = (VariableElement) e;
 TypeElement parent = (TypeElement) variable.getEnclosingElement();
 
 List<VariableElement> members;
 if (bindingClasses.containsKey(parentClass)) {
 members = bindingClasses.get(parentClass);
 } else {
 members = new ArrayList<>();
 bindingClasses.put(parentClass, members);
 }
 members.add(variable);
 } // .. generate code .. }
  • 37. Processing… @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 if (annotations.isEmpty()) {
 return true;
 }
 
 Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>();
 for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) {
 VariableElement variable = (VariableElement) e;
 TypeElement parent = (TypeElement) variable.getEnclosingElement();
 
 List<VariableElement> members;
 if (bindingClasses.containsKey(parentClass)) {
 members = bindingClasses.get(parentClass);
 } else {
 members = new ArrayList<>();
 bindingClasses.put(parentClass, members);
 }
 members.add(variable);
 } // .. generate code .. }
  • 38. Processing… @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 if (annotations.isEmpty()) {
 return true;
 }
 
 Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>();
 for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) {
 VariableElement variable = (VariableElement) e;
 TypeElement parent = (TypeElement) variable.getEnclosingElement();
 
 List<VariableElement> members;
 if (bindingClasses.containsKey(parentClass)) {
 members = bindingClasses.get(parentClass);
 } else {
 members = new ArrayList<>();
 bindingClasses.put(parentClass, members);
 }
 members.add(variable);
 } // .. generate code .. }
  • 39. Processing… @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 if (annotations.isEmpty()) {
 return true;
 }
 
 Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>();
 for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) {
 VariableElement variable = (VariableElement) e;
 TypeElement parent = (TypeElement) variable.getEnclosingElement();
 
 List<VariableElement> members;
 if (bindingClasses.containsKey(parentClass)) {
 members = bindingClasses.get(parentClass);
 } else {
 members = new ArrayList<>();
 bindingClasses.put(parentClass, members);
 }
 members.add(variable);
 } // .. generate code .. }
  • 40. Approach 1. Define the @Bind annotation. 2. Extend AbstractProcessor to create our annotation processor for @Bind. 3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes. 4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.
  • 41. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 } catch (IOException e) {
 throw new RuntimeException(e);
 } }
  • 42. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 } catch (IOException e) {
 throw new RuntimeException(e);
 } }
  • 43. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 } catch (IOException e) {
 throw new RuntimeException(e);
 } }
  • 44. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 } catch (IOException e) {
 throw new RuntimeException(e);
 } } 😱
  • 45. There has to be a better way!?
  • 46. There is a better way.
  • 49. JavaPoet • Builder-pattern approach to programmatically defining a class and its fields/methods. • Automatically manages the classes needed for import. • When you’re ready, it will write clean & readable Java source to an OutputStream/Writer.
  • 50. JavaPoet - Hello World TypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld")
 .addModifiers(Modifier.PUBLIC)
 .addMethod(MethodSpec.methodBuilder("main")
 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
 .returns(void.class)
 .addParameter(String[].class, "args")
 .addStatement("System.out.println($S + args[0])", "Hello: ")
 .build());
 JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out); package jwf.soupladle;
 
 import java.lang.String;
 
 public class HelloWorld {
 public static void main(String[] args) {
 System.out.println("Hello: " + args[0]);
 }
 }

  • 51. JavaPoet - Hello World TypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld")
 .addModifiers(Modifier.PUBLIC)
 .addMethod(MethodSpec.methodBuilder("main")
 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
 .returns(void.class)
 .addParameter(String[].class, "args")
 .addStatement("System.out.println($S + args[0])", "Hello: ")
 .build());
 JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out); package jwf.soupladle;
 
 import java.lang.String;
 
 public class HelloWorld {
 public static void main(String[] args) {
 System.out.println("Hello: " + args[0]);
 }
 }

  • 52. JavaPoet - Hello World TypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld")
 .addModifiers(Modifier.PUBLIC)
 .addMethod(MethodSpec.methodBuilder("main")
 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
 .returns(void.class)
 .addParameter(String[].class, "args")
 .addStatement("System.out.println($S + args[0])", "Hello: ")
 .build());
 JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out); package jwf.soupladle;
 
 import java.lang.String;
 
 public class HelloWorld {
 public static void main(String[] args) {
 System.out.println("Hello: " + args[0]);
 }
 }

  • 53. JavaPoet - Hello World TypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld")
 .addModifiers(Modifier.PUBLIC)
 .addMethod(MethodSpec.methodBuilder("main")
 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
 .returns(void.class)
 .addParameter(String[].class, "args")
 .addStatement("System.out.println($S + args[0])", "Hello: ")
 .build());
 JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out); package jwf.soupladle;
 
 import java.lang.String;
 
 public class HelloWorld {
 public static void main(String[] args) {
 System.out.println("Hello: " + args[0]);
 }
 }

  • 54. JavaPoet - Hello World 👏 TypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld")
 .addModifiers(Modifier.PUBLIC)
 .addMethod(MethodSpec.methodBuilder("main")
 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
 .returns(void.class)
 .addParameter(String[].class, "args")
 .addStatement("System.out.println($S + args[0])", "Hello: ")
 .build());
 JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out); package jwf.soupladle;
 
 import java.lang.String;
 
 public class HelloWorld {
 public static void main(String[] args) {
 System.out.println("Hello: " + args[0]);
 }
 }

  • 55. Approach 1. Define the @Bind annotation. 2. Extend AbstractProcessor to create our annotation processor for @Bind. 3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes. 4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.
  • 56. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; }
  • 57. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; }
  • 58. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; }
  • 59. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; }
  • 60. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; }
  • 61. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; }
  • 62. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; } "target.$L = ($T) target.findViewById($L)"
  • 63. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; } "target.$L = ($T) target.findViewById($L)"
  • 64. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; } "target.$L = ($T) target.findViewById($L)"
  • 65. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; } "target.$L = ($T) target.findViewById($L)"
  • 66. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; }
  • 67. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; }
  • 68. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; }
  • 69. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; } 👀
  • 70. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; }
  • 71. @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 // .. process annotations .. try {
 JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle");
 
 TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
 
 for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) {
 TypeName typeParameter = ClassName.get(binding.getKey());
 
 MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
 .addParameter(typeParameter, "target");
 
 List<VariableElement> members = binding.getValue();
 for (VariableElement member : members) {
 Bind annotation = member.getAnnotation(Bind.class);
 TypeName castClass = ClassName.get(member.asType());
 bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value());
 }
 
 soupLadleBuilder.addMethod(bindingBuilder.build());
 }
 
 AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); 
 JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build();
 Writer out = jfo.openWriter();
 file.writeTo(out);
 out.flush();
 out.close();
 } catch (IOException e) {
 throw new RuntimeException(e);
 } return true; }
  • 72. We’ve got an annotation processor now!
  • 73. How do we tell the build process about it?
  • 74. Project/Module Config • The annotation processor and binding annotation class need to live in a “regular” java module. • Add the android-apt gradle plugin to your root build.gradle. • Add dependency records to your app’s build.gradle.
  • 75. Project/Module Config • The annotation processor and binding annotation class need to live in a “regular” java module. • Add the android-apt gradle plugin to your root build.gradle. • Add dependency records to your app’s build.gradle.
  • 77. SoupLadle Module 
 apply plugin: 'java'
 
 dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 compile 'com.squareup:javapoet:1.7.0'
 }
  • 80. Project/Module Config • The annotation processor and binding annotation class need to live in a “regular” java module. • Add the android-apt gradle plugin to your root build.gradle. • Add dependency records to your app’s build.gradle.
  • 81. Project build.gradle buildscript {
 repositories {
 jcenter()
 }
 dependencies {
 classpath 'com.android.tools.build:gradle:2.1.3'
 classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
 }
 }
 
 allprojects {
 repositories {
 jcenter()
 }
 }
 
 task clean(type: Delete) {
 delete rootProject.buildDir
 }
  • 82. Project build.gradle buildscript {
 repositories {
 jcenter()
 }
 dependencies {
 classpath 'com.android.tools.build:gradle:2.1.3'
 classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
 }
 }
 
 allprojects {
 repositories {
 jcenter()
 }
 }
 
 task clean(type: Delete) {
 delete rootProject.buildDir
 }
  • 83. Project/Module Config • The annotation processor and binding annotation class need to live in a “regular” java module. • Add the android-apt gradle plugin to your root build.gradle. • Add dependency records to your app’s build.gradle.
  • 84. App build.gradle apply plugin: 'com.android.application'
 apply plugin: 'com.neenbedankt.android-apt'
 
 android {
 compileSdkVersion 24
 buildToolsVersion "24.0.2"
 
 defaultConfig {
 applicationId "jwf.soupladle.example"
 minSdkVersion 16
 targetSdkVersion 24
 versionCode 1
 versionName "1.0"
 }
 buildTypes {
 // .. your build types ..
 }
 }
 
 dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 compile 'com.android.support:appcompat-v7:24.1.1'
 
 apt project(':library')
 provided project(':library')
 }
  • 85. App build.gradle apply plugin: 'com.android.application'
 apply plugin: 'com.neenbedankt.android-apt'
 
 android {
 compileSdkVersion 24
 buildToolsVersion "24.0.2"
 
 defaultConfig {
 applicationId "jwf.soupladle.example"
 minSdkVersion 16
 targetSdkVersion 24
 versionCode 1
 versionName "1.0"
 }
 buildTypes {
 // .. your build types ..
 }
 }
 
 dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 compile 'com.android.support:appcompat-v7:24.1.1'
 
 apt project(':library')
 provided project(':library')
 }
  • 86. App build.gradle apply plugin: 'com.android.application'
 apply plugin: 'com.neenbedankt.android-apt'
 
 android {
 compileSdkVersion 24
 buildToolsVersion "24.0.2"
 
 defaultConfig {
 applicationId "jwf.soupladle.example"
 minSdkVersion 16
 targetSdkVersion 24
 versionCode 1
 versionName "1.0"
 }
 buildTypes {
 // .. your build types ..
 }
 }
 
 dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 compile 'com.android.support:appcompat-v7:24.1.1'
 
 apt project(':library')
 provided project(':library')
 }
  • 89. MainActivity.java package jwf.soupladle.example;
 
 import android.support.v7.app.AppCompatActivity;
 import android.os.Bundle;
 import android.widget.TextView;
 
 import jwf.soupladle.Bind;
 
 public class MainActivity extends AppCompatActivity {
 @Bind(R.id.hello_world)
 public TextView textView;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 }
 }
  • 90. MainActivity.java package jwf.soupladle.example;
 
 import android.support.v7.app.AppCompatActivity;
 import android.os.Bundle;
 import android.widget.TextView;
 
 import jwf.soupladle.Bind;
 
 public class MainActivity extends AppCompatActivity {
 @Bind(R.id.hello_world)
 public TextView textView;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 }
 }
  • 92. A wild SoupLadle.java Appears! package jwf.soupladle;
 
 import android.widget.TextView;
 import java.lang.SuppressWarnings;
 import jwf.soupladle.example.MainActivity;
 
 @SuppressWarnings("ResourceType")
 public final class SoupLadle {
 public static final void bind(MainActivity target) {
 target.textView = (TextView) target.findViewById(2131427412);
 }
 }

  • 93. MainActivity.java package jwf.soupladle.example;
 
 import android.support.v7.app.AppCompatActivity;
 import android.os.Bundle;
 import android.widget.TextView;
 
 import jwf.soupladle.Bind;
 import jwf.soupladle.SoupLadle;
 
 public class MainActivity extends AppCompatActivity {
 @Bind(R.id.hello_world)
 public TextView textView;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 SoupLadle.bind(this);
 textView.setText("The binding worked!");
 }
 }
  • 94.
  • 95.
  • 96. Thank you! Questions? Twitter: @jasonwyatt github.com/jasonwyatt bandcamp.com/jasonwyatt Source Code available at: github.com/jasonwyatt/Soup-Ladle