Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Java annotation processor

Avatar for Jaime Toca Jaime Toca
October 12, 2016

Java annotation processor

Avatar for Jaime Toca

Jaime Toca

October 12, 2016
Tweet

More Decks by Jaime Toca

Other Decks in Programming

Transcript

  1. Parcelable - Great way to serialize java objects between contexts

    - 10x time faster than java serializable - A lot of boilerplate code Money.class public class Money implements Parcelable { private Currency currency; private long amount; public Money(Currency currency, long amount){ this.currency = currency; this.amount = amount; }
  2. Parcelable Money.class @Override public int describeContents() { return 0; }

    @Override public void writeToParcel(Parcel dest, int flags) { dest.writeSerializable(this.currency); dest.writeLong(this.amount); } protected Money(Parcel in) { this.currency = (Currency) in.readSerializable(); this.amount = in.readLong(); }
  3. Parcelable Money.class public static final Parcelable.Creator<Money> CREATOR = new Parcelable.Creator<Money>()

    { @Override public Money createFromParcel(Parcel source) { return new Money(source); } @Override public Money[] newArray(int size) { return new Money[size]; } };
  4. Parceler @Parcel public class Money { private Currency currency; private

    long amount; public Money(Currency currency, long amount){ this.currency = currency; this.amount = amount; } public Currency getCurrency() { return currency; } public long getAmount() { return amount; } } Bundle bundle = new Bundle(); bundle.putParcelable("Money", Parcels.wrap(money)); Money money = Parcels.unwrap(getIntent(). getParcelableExtra("Money"));
  5. ButterKnife - Eliminate findViewById with @BindView - Group multiples views

    in a list or array - Eliminate anonymous inner-classes for listeners by annotating methods with @OnClick @BindView(R.id.pass) EditText password; @OnClick(R.id.login) void login() { //call loginpresenter } password = (EditText) findViewById(R.id.pass); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } });
  6. Value Types public final class Money { private Currency currency;

    private long amount; public Money(Currency currency, long amount) { this.currency = currency; this.amount = amount; } public Currency getCurrency() { return currency; } public long getAmount() { return amount; } } Money money1 = new Money(esCurrency,1123); Money money2 = new Money(esCurrency,1123); assert(money1.equals(money2)); //fails
  7. AutoValue @Override public boolean equals(Object o) { if (this ==

    o) return true; if (o == null || getClass() != o.getClass()) return false; Money money = (Money) o; if (Double.compare(money.amount, amount) != 0) return false; return currency.equals(money.currency); } @Override public int hashCode() { int result; long temp; result = currency.hashCode(); result = 31 * result + (int) (amount ^ (amount >>> 32)); return result; } } @Override public String toString() { return "Money{" + "currency=" + currency + ", amount=" + amount + '}'; }
  8. AutoValue @AutoValue public abstract class Money { public abstract Currency

    currency(); public abstract long amount(); public static Money create(Currency currency, long amount) { return new AutoValue_Money(currency, amount); } } - Removes boilerplate code - You can add additional code to this class if you want - Hidden benefit (no need for testing)
  9. MyView.java @Override protected void initOneCMSText(View view) { mTvVariable2.setText(LocalizablesFacade.getString(getActivity(), OneCMSKeys.KEYVARIABLE1)); mTvVariable3.setText(LocalizablesFacade.getString(getActivity(),

    OneCMSKeys.KEYVARIABLE2)); mTvVariable4.setText(LocalizablesFacade.getString(getActivity(), OneCMSKeys.KEYVARIABLE3)); mTvVariable5.setText(LocalizablesFacade.getString(getActivity(), OneCMSKeys.KEYVARIABLE4)); mTvVariable6.setText(LocalizablesFacade.getString(getActivity(), OneCMSKeys.KEYVARIABLE5)); mTvVariable7.setText(LocalizablesFacade.getString(getActivity(), OneCMSKeys.KEYVARIABLE6)); ……. } Content Processor
  10. Content Processor MyView.java @InjectContent(key = KEYVARIABLE1) TextView mTvVariable1 @InjectContent(key =

    KEYVARIABLE2) TextView mTvVariable2 @InjectContent(key = KEYVARIABLE3) TextView mTvVariable3 …. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); InitContent.InjectMyViewContent(this); }
  11. The processor public class ContentProcessor extends AbstractProcessor { ……... }

    public abstract class AbstractProcessor implements Processor { public Set<String> getSupportedAnnotationTypes(); public SourceVersion getSupportedSourceVersion(); public synchronized void init(ProcessingEnvironment processingEnv); public abstract boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv); }
  12. The processor @Override public Set<String> getSupportedAnnotationTypes() { return Collection.singleton(InjectContent.class.getCanonicalName()); }

    @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); }
  13. The processor @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv);

    messager = processingEnv.getMessager(); filer = processingEnv.getFiler(); elementUtils = processingEnv.getElementUtils(); typeUtils = processingEnv.getTypeUtils(); }
  14. The processor @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment

    roundEnv) { ArrayList<AnnotatedField> annotatedFields = new ArrayList<>(); for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(InjectContent.class)) { String variableName = annotatedElement.toString(); TypeElement annotatedFieldData = (TypeElement) annotatedElement.getEnclosingElement(); String oneCMSKey = annotatedElement.getAnnotation(InjectContent.class).key(); annotatedFields.add(buildAnnotatedField(variableName , annotatedFieldData, oneCMSKey)); …………. } if (!annotatedFields.isEmpty()){ generateCode(annotatedFields); } ………...
  15. Java Poet: Code generation API - Developed by Square -

    Code generation becomes easy - Uses fluent API with builders - Based on Specs - TypeSpecs - MethodSpecs - ParameterSpec - FieldSpec
  16. Java Poet: Code generation API package com.example.helloworld; public final class

    HelloWorld { public static void main(String[] args) { System.out.println("Hello, JavaPoet!"); } } MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!") .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main) .build(); JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build();
  17. Code generation - We want to generate a class with

    N methods - Each method will set the oneCMS content for all the fields in that View public final class InitContent { public static void injectMainActivityContent(MainActivity mainActivity) { mainActivity.mTvHello.setText(//access localizable); mainActivity.mTvBye.setText(//access localizable); mainActivity.mTvAwesome.setText(//access localizable); mainActivity.mTvWhatever.setText(//access localizable); } public static void injectSecondActivityContent(SecondActivity secondActivity) { secondActivity.mTvHello.setText(//access localizable); secondActivity.mTvWhatever.setText(//access localizable); } …... }
  18. Code generation private void generateCode(ArrayList<AnnotatedField> annotatedFields) throws NoPackageNameException, IOException {

    String packageName = Utils.getPackageName(elementUtils, nnotatedFields.get(0).getTypeElement()); TypeSpec generatedClass = CodeGenerator.generateClass(annotatedFields); JavaFile javaFile = builder(packageName, generatedClass).build(); javaFile.writeTo(filer); }
  19. Code generation public static TypeSpec generateClass(List <AnnotatedField> annotatedFields) { TypeSpec.Builder

    builder = classBuilder(CLASS_NAME).addModifiers(PUBLIC, FINAL); HashMap<String, List <annotatedField>> bindingClass = bindClassesWithAnnotation(annotatedFields); …. for (ViewClass viewClass : viewClasses) { builder.addMethod(createInitContentMethod(bindingClass)) } return builder.build(); } private static MethodSpec createInitContentMethod(List <AnnotatedField> annotatedFields){ …. String methodName = PREFIX_METHOD_INIT + annotatedField.getClassName() + SUFIX_METHOD_INIT; String methodParameterName = annotatedField.getClassName().toLowerCase(); …. return methodBuilder(methodName) .addModifiers(PUBLIC, STATIC) .addParameter(get(annotatedField.getTypeElement().asType()), methodParameterName) .addStatement(methodParameterName +"." + variableName + SET_TEXT + "(\"LocalizableAccessHere\")") CONCATENATE THIS .returns(TypeName.VOID) .build(); }
  20. References - https://realm.io/news/360andev-ryan-harter-eliminate-boilerplate/ - https://www.youtube.com/watch?v=dOcs-NKK-RA - https://www.youtube.com/watch?v=43FFfTyDYEg - http://hannesdorfmann.com/annotation-processing/annotationprocessing101 -

    http://blog.stablekernel.com/the-10-step-guide-to-annotation-processing-in-an droid-studio - http://hannesdorfmann.com/annotation-processing/annotationprocessing101 - https://www.youtube.com/watch?v=dBUAqPs0TB0