Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Use code generation to make better apps

Avatar for Dylan Drost Dylan Drost
October 08, 2016

Use code generation to make better apps

Slides for my talk. Where I explain how and why we can code generation in our Android Apps.

Avatar for Dylan Drost

Dylan Drost

October 08, 2016

More Decks by Dylan Drost

Other Decks in Technology

Transcript

  1. Use code ​generation to make better apps Why write it

    yourself when a computer can do it better
  2. About me • Android Developer • Do some occasional backend

    work in php • Fixes things on bettyskitchen.nl • Work @Karify • @aegis321
  3. What I gonna talk about • What does code generation

    mean • What use cases are there • Why would we want to use it • How do we generate our code • Existing tools and libraries • How do we implement it in our projects
  4. What use cases are there The data access layer better

    known as the sqlite database or sharedpreferences. Hand written code: ContentValues values = new ContentValues(1); values.put("about", "student"); String sql = "age > ?" db.update("person_table", values, sql, new String[]{Integer.toString(21)}); Generated code: int rows = data.update(Person.class) .set(Person.ABOUT, "student") .where(Person.AGE.lt(21)).get().value();
  5. What use cases are there Serializing and deserializing “data is

    usually really boring”. public class Person implements Parcelable { protected Person(Parcel in) {...} @Override public void writeToParcel(Parcel dest, int flags) {...} @Override public int describeContents() { return 0; } public static final Creator<Person> CREATOR = new Creator<Person>() { @Override public Person createFromParcel(Parcel in) { return new Person(in); } @Override public Person[] newArray(int size) { return new Person[size]; } }; } @AutoValue public abstract class Person implements Parcelable { }
  6. What use cases are there Binding Things <?xml version="1.0" encoding="utf-8"?>

    <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="com.example.User"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.lastName}"/> </LinearLayout> </layout> @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MainActivityBinding binding = DataBindView(this, R.layout.main_activity); User user = new User("Test", "User"); binding.setUser(user); }
  7. What use cases are there Binding Things class ExampleActivity extends

    Activity { @BindView(R.id.title) TextView title; @BindView(R.id.subtitle) TextView subtitle; @BindView(R.id.footer) TextView footer; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.bind(this); // or the new ExampleActivity_Binding(this, getwindow().getDecorView()) // TODO Use fields... } } class ExampleActivity extends Activity { @BindString(R.string.title) String title; @BindDrawable(R.drawable.graphic) Drawable graphic; @BindColor(R.color.red) int red; // int or ColorStateList field @BindDimen(R.dimen.spacer) int spacer; // int (for pixel size) field @BindDimen(R.dimen.spacer_exact) Float spacer; // float (for exact value) field // ... }
  8. What use cases are there Binding Things class ExampleActivity extends

    Activity { @BindString(R.string.title) String title; @BindDrawable(R.drawable.graphic) Drawable graphic; @BindColor(R.color.red) int red; // int or ColorStateList field @BindDimen(R.dimen.spacer) int spacer; // int (for pixel size) field @BindDimen(R.dimen.spacer_exact) Float spacer; // float (for exact value) field // ... }
  9. What use cases are there Dependency Injection class Thermosiphon implements

    Pump { private final Heater heater; @Inject Thermosiphon(Heater heater) { this.heater = heater; } ... } @Module(includes = PumpModule.class) class DripCoffeeModule { @Provides @Singleton Heater provideHeater() { return new ElectricHeater(); } } @Component(modules = DripCoffeeModule.class) interface CoffeeShop { CoffeeMaker maker(); } public class CoffeeApp { public static void main(String[] args) { CoffeeShop coffeeShop = DaggerCoffeeShop.create(); coffeeShop.maker().brew(); } }
  10. Why would we want to use it • Repetitive code

    • Boilerplate reduction • Programmer error ← git blame • Compile time exceptions • Code optimizations
  11. How do we generate our code Parsing a files --

    src/main/sqldelight/com/example/HockeyPlayer.sq CREATE TABLE hockey_player ( _id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, NUMBER INTEGER NOT NULL, name TEXT NOT NULL ); -- Further SQL statements are proceeded by an identifier. This will be used to name the constant -- in the generated Java code. select_by_name: SELECT * FROM hockey_player WHERE name = ?;
  12. How do we generate our code Annotation Processing @AutoValue @Entity

    abstract class Person { @AutoValue.Builder static abstract class Builder { abstract Builder setId(int id); abstract Builder setName(String name); abstract Builder setEmail(String email); abstract Person build(); } static Builder builder() { return new AutoValue_Person.Builder(); } @Key abstract int getId(); abstract String getName(); abstract String getEmail(); }
  13. How do we generate our code Generate code from java

    code with 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(); javaFile.writeTo(System.out);
  14. Existing tools and libraries • Dagger ← Dependency Inject •

    AutoValue ← Immutable Type generator • Requery ← ORM • SQLDelight ← Query and Cursor mapper • ButterKnife ← View and resource binding/injecting. • And many more android-arsenal APT category
  15. How do we implement it in our projects And now

    the tricky part using all those tools in your projects. It's actually really easy.
  16. How do we implement it in our projects buildscript {

    repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:2.2.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' } } apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' ....... dependencies { apt 'com.google.dagger:dagger-compiler:2.7' compile 'com.google.dagger:dagger:2.7' provided 'javax.annotation:jsr250-api:1.0' apt 'com.google.auto.value:auto-value:1.3' provided 'com.google.auto.value:auto-value:1.3' compile 'com.jakewharton:butterknife:8.4.0' apt 'com.jakewharton:butterknife-compiler:8.4.0' }
  17. Links • @Elimintate(BoilerPlate) 360|Andev by Ryan Harter • AutoValue •

    ButterKnife • Dagger 2 • Requery • Wikipedia: Code Generation compiler