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

Mechanisms of Metaprogramming (Chicago Roboto 2019)

Mechanisms of Metaprogramming (Chicago Roboto 2019)

Metaprogramming is all around us on Android in the form of reflection, annotation processing, Gradle plugins, and standalone tools. Some of the most popular and widespread libraries in use leverage one or more of these facilities. But none of these are the perfect tool for every problem. Each has advantages and disadvantages depending on how they’re used. And choosing the wrong one can actually harm the usability of your library.

This talk will compare and contrast the different approaches to metaprogramming, look at popular Android libraries and evaluate whether they made the right choice, and conclude with recommendations for those looking to use these techniques in the future.

Video: coming soon

Jake Wharton

April 25, 2019
Tweet

More Decks by Jake Wharton

Other Decks in Programming

Transcript

  1. Mechanisms of
    Metaprogramming
    @JakeWharton

    View full-size slide

  2. Metaprogramming

    View full-size slide

  3. Metaprogramming
    Runtime

    View full-size slide

  4. Metaprogramming
    Runtime Compile-time
    vs.

    View full-size slide

  5. Metaprogramming
    Runtime Compile-time
    vs.
    Additive

    View full-size slide

  6. Metaprogramming
    Runtime Compile-time
    vs.
    Additive Mutating
    vs.

    View full-size slide

  7. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating

    View full-size slide

  8. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating

    View full-size slide

  9. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser

    View full-size slide

  10. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator

    View full-size slide

  11. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  12. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  13. SomeLibrary.doSomething(User.class);

    View full-size slide

  14. SomeLibrary.doSomething(User.class);
    AnotherLibrary.doSomething(this);

    View full-size slide

  15. SomeLibrary.doSomething(User.class);
    AnotherLibrary.doSomething(this);
    class AnotherLibrary {
    static void doSomething(Object instance) {
    Class> cls = instance.getClass();
    // ...
    }
    }

    View full-size slide

  16. for (Method method : moduleClass.getDeclaredMethods()) {
    Type returnType = method.getGenericReturnType();
    if ((method.getModifiers() & ABSTRACT) != 0) {
    if (method.getAnnotation(Binds.class) != null) {
    // ...
    } else if (method.getAnnotation(BindsOptionalOf.class) != null) {
    // ...
    }
    } else {
    if ((method.getModifiers() & STATIC) == 0 && instance == null) {
    // ...
    }
    if (method.getAnnotation(Provides.class) != null) {
    // ...
    }
    }
    }

    View full-size slide

  17. for (Method method : moduleClass.getDeclaredMethods()) {
    Type returnType = method.getGenericReturnType();
    if ((method.getModifiers() & ABSTRACT) != 0) {
    if (method.getAnnotation(Binds.class) != null) {
    // ...
    } else if (method.getAnnotation(BindsOptionalOf.class) != null) {
    // ...
    }
    } else {
    if ((method.getModifiers() & STATIC) == 0 && instance == null) {
    // ...
    }
    if (method.getAnnotation(Provides.class) != null) {
    // ...
    }
    }
    }

    View full-size slide

  18. for (Method method : moduleClass.getDeclaredMethods()) {
    Type returnType = method.getGenericReturnType();
    if ((method.getModifiers() & ABSTRACT) != 0) {
    if (method.getAnnotation(Binds.class) != null) {
    // ...
    } else if (method.getAnnotation(BindsOptionalOf.class) != null) {
    // ...
    }
    } else {
    if ((method.getModifiers() & STATIC) == 0 && instance == null) {
    // ...
    }
    if (method.getAnnotation(Provides.class) != null) {
    // ...
    }
    }
    }

    View full-size slide

  19. for (Method method : moduleClass.getDeclaredMethods()) {
    Type returnType = method.getGenericReturnType();
    if ((method.getModifiers() & ABSTRACT) != 0) {
    if (method.getAnnotation(Binds.class) != null) {
    // ...
    } else if (method.getAnnotation(BindsOptionalOf.class) != null) {
    // ...
    }
    } else {
    if ((method.getModifiers() & STATIC) == 0 && instance == null) {
    // ...
    }
    if (method.getAnnotation(Provides.class) != null) {
    // ...
    }
    }
    }

    View full-size slide

  20. for (Method method : moduleClass.getDeclaredMethods()) {
    Type returnType = method.getGenericReturnType();
    if ((method.getModifiers() & ABSTRACT) != 0) {
    if (method.getAnnotation(Binds.class) != null) {
    // ...
    } else if (method.getAnnotation(BindsOptionalOf.class) != null) {
    // ...
    }
    } else {
    if ((method.getModifiers() & STATIC) == 0 && instance == null) {
    // ...
    }
    if (method.getAnnotation(Provides.class) != null) {
    // ...
    }
    }
    }

    View full-size slide

  21. for (Method method : moduleClass.getDeclaredMethods()) {
    Type returnType = method.getGenericReturnType();
    if ((method.getModifiers() & ABSTRACT) != 0) {
    if (method.getAnnotation(Binds.class) != null) {
    // ...
    } else if (method.getAnnotation(BindsOptionalOf.class) != null) {
    // ...
    }
    } else {
    if ((method.getModifiers() & STATIC) == 0 && instance == null) {
    // ...
    }
    if (method.getAnnotation(Provides.class) != null) {
    // ...
    }
    }
    }

    View full-size slide

  22. Reflection
    • Only access to the shape of classes, methods, and fields

    View full-size slide

  23. Reflection
    • Only access to the shape of classes, methods, and fields
    • No access to content of methods

    View full-size slide

  24. Reflection
    • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    • Methods can be invoked, fields can be read/written

    View full-size slide

  25. Reflection
    • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    • Methods can be invoked, fields can be read/written
    • Shape is immutable, no new methods, fields, or types

    View full-size slide

  26. Reflection
    • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    • Methods can be invoked, fields can be read/written
    • Shape is immutable, no new methods, fields, or types
    • Annotations can be read from method, field, parameters, and types

    View full-size slide

  27. Reflection
    • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    • Methods can be invoked, fields can be read/written
    • Shape is immutable, no new methods, fields, or types
    • Annotations can be read from method, field, parameters, and types
    • Relatively slow compared to using methods and fields normally

    View full-size slide

  28. public T create(final Class service) {
    Utils.validateServiceInterface(service);
    return (T) Proxy.newProxyInstance(
    service.getClassLoader(),
    new Class>[] { service },
    new InvocationHandler() {
    @Override public @Nullable Object invoke(Object proxy,
    Method method, @Nullable Object[] args) throws Throwable {
    // If the method is from Object then defer to normal invocation.
    if (method.getDeclaringClass() == Object.class) {
    return method.invoke(this, args);
    }
    return loadServiceMethod(method).invoke(args);
    }
    });
    }

    View full-size slide

  29. public T create(final Class service) {
    Utils.validateServiceInterface(service);
    return (T) Proxy.newProxyInstance(
    service.getClassLoader(),
    new Class>[] { service },
    new InvocationHandler() {
    @Override public @Nullable Object invoke(Object proxy,
    Method method, @Nullable Object[] args) throws Throwable {
    // If the method is from Object then defer to normal invocation.
    if (method.getDeclaringClass() == Object.class) {
    return method.invoke(this, args);
    }
    return loadServiceMethod(method).invoke(args);
    }
    });
    }

    View full-size slide

  30. public T create(final Class service) {
    Utils.validateServiceInterface(service);
    return (T) Proxy.newProxyInstance(
    service.getClassLoader(),
    new Class>[] { service },
    new InvocationHandler() {
    @Override public @Nullable Object invoke(Object proxy,
    Method method, @Nullable Object[] args) throws Throwable {
    // If the method is from Object then defer to normal invocation.
    if (method.getDeclaringClass() == Object.class) {
    return method.invoke(this, args);
    }
    return loadServiceMethod(method).invoke(args);
    }
    });
    }

    View full-size slide

  31. public T create(final Class service) {
    Utils.validateServiceInterface(service);
    return (T) Proxy.newProxyInstance(
    service.getClassLoader(),
    new Class>[] { service },
    new InvocationHandler() {
    @Override public @Nullable Object invoke(Object proxy,
    Method method, @Nullable Object[] args) throws Throwable {
    // If the method is from Object then defer to normal invocation.
    if (method.getDeclaringClass() == Object.class) {
    return method.invoke(this, args);
    }
    return loadServiceMethod(method).invoke(args);
    }
    });
    }

    View full-size slide

  32. public T create(final Class service) {
    Utils.validateServiceInterface(service);
    return (T) Proxy.newProxyInstance(
    service.getClassLoader(),
    new Class>[] { service },
    new InvocationHandler() {
    @Override public @Nullable Object invoke(Object proxy,
    Method method, @Nullable Object[] args) throws Throwable {
    // If the method is from Object then defer to normal invocation.
    if (method.getDeclaringClass() == Object.class) {
    return method.invoke(this, args);
    }
    return loadServiceMethod(method).invoke(args);
    }
    });
    }

    View full-size slide

  33. public T create(final Class service) {
    Utils.validateServiceInterface(service);
    return (T) Proxy.newProxyInstance(
    service.getClassLoader(),
    new Class>[] { service },
    new InvocationHandler() {
    @Override public @Nullable Object invoke(Object proxy,
    Method method, @Nullable Object[] args) throws Throwable {
    // If the method is from Object then defer to normal invocation.
    if (method.getDeclaringClass() == Object.class) {
    return method.invoke(this, args);
    }
    return loadServiceMethod(method).invoke(args);
    }
    });
    }

    View full-size slide

  34. Reflection
    • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    • Methods can be invoked, fields can be read/written
    • Shape is immutable, no new methods, fields, or types
    • Annotations can be read from method, field, parameters, and types
    • Relatively slow compared to using methods and fields normally

    View full-size slide

  35. Reflection
    • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    • Methods can be invoked, fields can be read/written
    • Shape is immutable, no new methods, fields, or types
    • Annotations can be read from method, field, parameters, and types
    • Relatively slow compared to using methods and fields normally
    • Can create implementations of interfaces

    View full-size slide

  36. Reflection
    • GOOD: Debug tools and testing where performance is less of a concern

    View full-size slide

  37. Reflection
    • GOOD: Debug tools and testing where performance is less of a concern
    • GOOD: When dominated by a slower operation like network I/O

    View full-size slide

  38. Reflection
    • GOOD: Debug tools and testing where performance is less of a concern
    • GOOD: When dominated by a slower operation like network I/O
    • OKAY: When on critical path and used infrequently

    View full-size slide

  39. Reflection
    • GOOD: Debug tools and testing where performance is less of a concern
    • GOOD: When dominated by a slower operation like network I/O
    • OKAY: When on critical path and used infrequently
    • MEH: When used with R8 or ProGuard

    View full-size slide

  40. Reflection
    • GOOD: Debug tools and testing where performance is less of a concern
    • GOOD: When dominated by a slower operation like network I/O
    • OKAY: When on critical path and used infrequently
    • MEH: When used with R8 or ProGuard
    • BAD: When on critical path and used heavily

    View full-size slide

  41. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  42. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  43. SomeLibrary.doSomething(R.raw.stuff);

    View full-size slide

  44. SomeLibrary.doSomething(R.raw.stuff);
    AnotherLibrary.doSomething("foo.txt");

    View full-size slide

  45. SomeLibrary.doSomething(R.raw.stuff);
    AnotherLibrary.doSomething("foo.txt");
    Response response = // ...
    Library.doSomething(response.body().string());

    View full-size slide

  46. {"v":"4.8.0","fr":29.9700012207031,"ip":0,"op":61.0000024845809,"w":150,"h":150,"nm":"Name","ddd":
    0,"assets":[],"fonts":{"list":[{"origin":0,"fPath":"","fClass":"","fFamily":"Comic
    Neue","fWeight":"","fStyle":"Regular","fName":"ComicNeue","ascent":69.6990966796875}]},"layers":[{"ddd":
    0,"ind":1,"ty":5,"nm":"NAME","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":
    0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[10.5,15,0],"e":
    [10.5,147,0],"to":[0,22,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":
    0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[10.5,147,0],"e":[10.5,15,0],"to":[0,0,0],"ti":[0,22,0]},
    {"t":30.0000012219251}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":
    [0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":
    ["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":
    [100,100,100],"e":[196,196,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":
    [0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":
    ["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":15,"s":
    [196,196,100],"e":[100,100,100]},{"t":30.0000012219251}]}},"ao":0,"t":{"d":{"k":[{"s":{"s":
    14,"f":"ComicNeue","t":"NAME","j":0,"tr":0,"lh":16.8,"ls":0,"fc":[0.92,0,0]},"t":0}]},"p":{},"m":{"g":
    1,"a":{"a":0,"k":[0,0]}},"a":[{"s":{"t":0,"xe":{"a":0,"k":0},"ne":{"a":0,"k":0},"a":{"a":0,"k":100},"b":
    1,"rn":0,"sh":1,"r":1},"a":{"fc":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":
    [0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[1,0,0,1],"e":[0,1,0,1]},{"i":{"x":[0.833],"y":
    [0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":15,"s":[0,1,0,1],"e":
    [0,0,1,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":
    ["0p833_0p833_0p167_0p167"],"t":30,"s":[0,0,1,1],"e":[0,1,0,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":
    [0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45,"s":[0,1,0,1],"e":[1,0,0,1]},{"t":
    60.0000024438501}]},"t":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":
    ["0p833_0p833_0p167_0p167"],"t":30,"s":[0],"e":[20]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":
    [0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45,"s":[20],"e":[0]},{"t":60.0000024438501}]}}}]},"ip":
    0,"op":61.0000024845809,"st":0,"bm":0,"sr":1}]}

    View full-size slide

  47. • Use any format / schema / language as input
    Parsing

    View full-size slide

  48. • Use any format / schema / language as input
    • Be mindful of forward/backward compatibility with inputs
    Parsing

    View full-size slide

  49. • Use any format / schema / language as input
    • Be mindful of forward/backward compatibility with inputs
    • Invoke existing framework APIs or ones from a library
    Parsing

    View full-size slide

  50. Parsing
    • GOOD: Re-use output formats from existing tools

    View full-size slide

  51. Parsing
    • GOOD: Re-use output formats from existing tools
    • GOOD: Behavior can be loaded from external resource like a server

    View full-size slide

  52. Parsing
    • GOOD: Re-use output formats from existing tools
    • GOOD: Behavior can be loaded from external resource like a server
    • OKAY: When on critical path and used infrequently

    View full-size slide

  53. Parsing
    • GOOD: Re-use output formats from existing tools
    • GOOD: Behavior can be loaded from external resource like a server
    • OKAY: When on critical path and used infrequently
    • BAD: When on critical path and used heavily

    View full-size slide

  54. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  55. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  56. message Person {
    required uint64 id = 1;
    required string name = 2;
    optional string email = 3;
    }

    View full-size slide

  57. message Person {
    required uint64 id = 1;
    required string name = 2;
    optional string email = 3;
    }
    class Person {
    long getId() { .. }
    String getName() { .. }
    String getEmail() { .. }
    class Builder {
    Builder setId(long id) { .. }
    Builder setName(String name) { .. }
    Builder setEmail(String email) { .. }
    Person build() { .. }
    }
    }

    View full-size slide

  58. @AutoValue
    abstract class Person {
    static Person from(long id, String name, String email) {
    return new AutoValue_Person(id, name, email);
    }
    abstract long id();
    abstract String name();
    abstract String email();
    }

    View full-size slide

  59. @AutoValue
    abstract class Person {
    static Person from(long id, String name, String email) {
    return new AutoValue_Person(id, name, email);
    }
    abstract long id();
    abstract String name();
    abstract String email();
    }

    View full-size slide

  60. @AutoValue
    abstract class Person {
    static Person from(long id, String name, String email) {
    return new AutoValue_Person(id, name, email);
    }
    abstract long id();
    abstract String name();
    abstract String email();
    }

    View full-size slide

  61. @AutoValue
    abstract class Person {
    static Person from(long id, String name, String email) {
    return new AutoValue_Person(id, name, email);
    }
    abstract long id();
    abstract String name();
    abstract String email();
    }

    View full-size slide

  62. @AutoValue
    abstract class Person {
    static Person from(long id, String name, String email) {
    return new AutoValue_Person(id, name, email);
    }
    abstract long id();
    abstract String name();
    abstract String email();
    }
    final class AutoValue_Person extends Person {
    AutoValue_Person(long id, String name, String email) { .. }
    @Override abstract long id() { .. }
    @Override abstract String name() { .. }
    @Override abstract String email() { .. }
    // equals(), hashCode(), toString()...
    }

    View full-size slide

  63. • Only access to the shape of classes, methods, and fields
    Source Generator (Annotation Processor)

    View full-size slide

  64. • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    Source Generator (Annotation Processor)

    View full-size slide

  65. • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    • Shape of existing code is immutable, no new methods, fields, or types
    Source Generator (Annotation Processor)

    View full-size slide

  66. • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    • Shape of existing code is immutable, no new methods, fields, or types
    • Annotations can be read from method, field, parameters, and types
    Source Generator (Annotation Processor)

    View full-size slide

  67. • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    • Shape of existing code is immutable, no new methods, fields, or types
    • Annotations can be read from method, field, parameters, and types
    • Can generate any kind of type or a Java resource
    Source Generator (Annotation Processor)

    View full-size slide

  68. • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    • Shape of existing code is immutable, no new methods, fields, or types
    • Annotations can be read from method, field, parameters, and types
    • Can generate any kind of type or a Java resource
    • Avoid leaking generated code into API, treat as implementation detail
    Source Generator (Annotation Processor)

    View full-size slide

  69. • Only access to the shape of classes, methods, and fields
    • No access to content of methods
    • Shape of existing code is immutable, no new methods, fields, or types
    • Annotations can be read from method, field, parameters, and types
    • Can generate any kind of type or a Java resource
    • Avoid leaking generated code into API, treat as implementation detail
    • Use JavaPoet, KotlinPoet, auto-common, and/or compile-testing to build
    Source Generator (Annotation Processor)

    View full-size slide

  70. • Generated source code can be public API and should feel like a library
    Source Generator (Other)

    View full-size slide

  71. • Generated source code can be public API and should feel like a library
    • You can generate inputs to other tools like ProGuard rules or drawable XML
    Source Generator (Other)

    View full-size slide

  72. • Generated source code can be public API and should feel like a library
    • You can generate inputs to other tools like ProGuard rules or drawable XML
    • Use JavaPoet and KotlinPoet for source generation
    Source Generator (Other)

    View full-size slide

  73. • GOOD: Same performance and capability as hand-written code
    Source Generator

    View full-size slide

  74. • GOOD: Same performance and capability as hand-written code
    • GOOD: Relatively easy to create and change
    Source Generator

    View full-size slide

  75. • GOOD: Same performance and capability as hand-written code
    • GOOD: Relatively easy to create and change
    • GOOD: Works extremely well with R8 and ProGuard
    Source Generator

    View full-size slide

  76. • GOOD: Same performance and capability as hand-written code
    • GOOD: Relatively easy to create and change
    • GOOD: Works extremely well with R8 and ProGuard
    • OKAY: Can contribute a lot of methods, fields, and allocations
    Source Generator

    View full-size slide

  77. • GOOD: Same performance and capability as hand-written code
    • GOOD: Relatively easy to create and change
    • GOOD: Works extremely well with R8 and ProGuard
    • OKAY: Can contribute a lot of methods, fields, and allocations
    • MEH: Referencing generated code from annotation processors
    Source Generator

    View full-size slide

  78. • GOOD: Same performance and capability as hand-written code
    • GOOD: Relatively easy to create and change
    • GOOD: Works extremely well with R8 and ProGuard
    • OKAY: Can contribute a lot of methods, fields, and allocations
    • MEH: Referencing generated code from annotation processors
    • MEH: Build performance impact requires care
    Source Generator

    View full-size slide

  79. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  80. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  81. // search_item.xml
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    android:id="@+id/package_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/more_options"
    app:layout_constraintTop_toTopOf="parent"
    />
    android:id="@+id/class_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="@+id/package_name"
    app:layout_constraintEnd_toEndOf="@+id/package_name"
    app:layout_constraintTop_toBottomOf="@+id/package_name"
    app:layout_constraintBottom_toBottomOf="parent"
    />
    android:id="@+id/more_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_more_vert_black_24dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    />

    View full-size slide

  82. // search_item.xml





    View full-size slide

  83. // search_item.xml





    final class R {
    static final class id {
    static final int class_name = 0x7f0000001;
    static final int more_options = 0x7f000002;
    static final int package_name = 0x7f000003;
    }
    static final class layout {
    static final int search_item = 0x7f000004;
    }
    }

    View full-size slide

  84. // search_item.xml





    $ find build -name 'R.*'
    build/intermediates/r_class_jar/release/generateReleaseRFile/R.jar
    build/intermediates/r_class_jar/debug/generateDebugRFile/R.jar

    View full-size slide

  85. // search_item.xml





    $ find build -name 'R.*'
    build/intermediates/r_class_jar/release/generateReleaseRFile/R.jar
    build/intermediates/r_class_jar/debug/generateDebugRFile/R.jar
    $ javap -cp build/.../generateReleaseRFile/R.jar 'com.example.R$layout'
    public final class com.jakewharton.sdksearch.search.ui.R$layout {
    public static final int class_name;
    public static final int more_options;
    public static final int package_name;
    }

    View full-size slide

  86. Bytecode Generator
    • Same basic guidelines as source generation

    View full-size slide

  87. Bytecode Generator
    • Same basic guidelines as source generation
    • Use ASM or ByteBuddy, or some kind of IR

    View full-size slide

  88. Bytecode Generator
    • GOOD: Same or better performance and capability as hand-written code

    View full-size slide

  89. Bytecode Generator
    • GOOD: Same or better performance and capability as hand-written code
    • GOOD: Works extremely well with R8 and ProGuard

    View full-size slide

  90. Bytecode Generator
    • GOOD: Same or better performance and capability as hand-written code
    • GOOD: Works extremely well with R8 and ProGuard
    • GOOD: Avoids the need to run Java and Kotlin compiler

    View full-size slide

  91. Bytecode Generator
    • GOOD: Same or better performance and capability as hand-written code
    • GOOD: Works extremely well with R8 and ProGuard
    • GOOD: Avoids the need to run Java and Kotlin compiler
    • MEH: Somewhat hard to write and maintain

    View full-size slide

  92. Bytecode Generator
    • GOOD: Same or better performance and capability as hand-written code
    • GOOD: Works extremely well with R8 and ProGuard
    • GOOD: Avoids the need to run Java and Kotlin compiler
    • MEH: Somewhat hard to write and maintain
    • BAD: No source code to step through while debugging

    View full-size slide

  93. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  94. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  95. class TimedRunnable implements Comparable {
    final long execTime;
    @Override public int compareTo(TimedRunnable that) {
    return Long.compare(execTime, that.execTime);
    }
    }

    View full-size slide

  96. class TimedRunnable implements Comparable {
    final long execTime;
    @Override public int compareTo(TimedRunnable that) {
    return Long.compare(execTime, that.execTime);
    }
    }

    View full-size slide

  97. class TimedRunnable implements Comparable {
    final long execTime;
    @Override public int compareTo(TimedRunnable that) {
    return Long.compare(execTime, that.execTime);
    }
    }
    java/lang/Long.compare --> io/reactivex/internal/java/lang/Long.compare

    View full-size slide

  98. class TimedRunnable implements Comparable {
    final long execTime;
    @Override public int compareTo(TimedRunnable that) {
    return Long.compare(execTime, that.execTime);
    }
    }
    java/lang/Long.compare --> io/reactivex/internal/java/lang/Long.compare
    class TimedRunnable implements Comparable {
    final long execTime;
    @Override public int compareTo(TimedRunnable that) {
    return io.reactivex.internal.java.lang.Long.compare(
    execTime, that.execTime);
    }
    }

    View full-size slide

  99. Source Transformer
    • Unlike annotation processing, you have access to method body content

    View full-size slide

  100. Source Transformer
    • Unlike annotation processing, you have access to method body content
    • Free to change, update, and delete any types, method, fields, and code

    View full-size slide

  101. Source Transformer
    • Unlike annotation processing, you have access to method body content
    • Free to change, update, and delete any types, method, fields, and code
    • Use Spoon for complicated transformations, regex for simple ones

    View full-size slide

  102. Source Transformer
    • GOOD: Can be as simple or complex as needed

    View full-size slide

  103. Source Transformer
    • GOOD: Can be as simple or complex as needed
    • GOOD: Easy to get started and experiment with

    View full-size slide

  104. Source Transformer
    • GOOD: Can be as simple or complex as needed
    • GOOD: Easy to get started and experiment with
    • MEH: Limited applicability

    View full-size slide

  105. Source Transformer
    • GOOD: Can be as simple or complex as needed
    • GOOD: Easy to get started and experiment with
    • MEH: Limited applicability
    • BAD: Transformations are brittle and prone to breakage

    View full-size slide

  106. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  107. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  108. $ javap -c build/classes/main/io/reactivex/internal/schedulers/TrampolineScheduler\$TimedRunnable.class
    Compiled from "TrampolineScheduler.java"
    final class io.reactivex.internal.schedulers.TrampolineScheduler$TimedRunnable implements
    java.lang.Comparable {
    final java.lang.Runnable run;
    final long execTime;
    public int compareTo(io.reactivex.internal.schedulers.TrampolineScheduler$TimedRunnable);
    Code:
    0: aload_0
    1: getfield #5 // Field execTime:J
    4: aload_1
    5: getfield #5 // Field execTime:J
    8: invokestatic #7 // Method java/lang/Long.compare:(JJ)I
    11: ireturn
    }

    View full-size slide

  109. $ javap -c build/classes/main/io/reactivex/internal/schedulers/TrampolineScheduler\$TimedRunnable.class
    Compiled from "TrampolineScheduler.java"
    final class io.reactivex.internal.schedulers.TrampolineScheduler$TimedRunnable implements
    java.lang.Comparable {
    final java.lang.Runnable run;
    final long execTime;
    public int compareTo(io.reactivex.internal.schedulers.TrampolineScheduler$TimedRunnable);
    Code:
    0: aload_0
    1: getfield #5 // Field execTime:J
    4: aload_1
    5: getfield #5 // Field execTime:J
    8: invokestatic #7 // Method java/lang/Long.compare:(JJ)I
    11: ireturn
    }

    View full-size slide

  110. final class BackportingMethodCallRemapper extends ClassVisitor {
    BackportingMethodCallRemapper(ClassVisitor cv) {
    super(ASM5, cv);
    }
    @Override public MethodVisitor visitMethod(int access, String name, String desc,
    String signature, String[] exceptions) {
    MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
    if (mv == null) {
    return null;
    }
    return new MethodVisitor(ASM5, mv) {
    @Override public void visitMethodInsn(int opcode, String owner, String name,
    String desc, boolean itf) {
    if ("java/lang/Long".equals(owner) && "compare".equals(name)) {
    owner = "io/reactivex/internal/java/lang/Long";
    }
    super.visitMethodInsn(opcode, owner, name, desc, itf);
    }
    };
    }
    }

    View full-size slide

  111. final class BackportingMethodCallRemapper extends ClassVisitor {
    BackportingMethodCallRemapper(ClassVisitor cv) {
    super(ASM5, cv);
    }
    @Override public MethodVisitor visitMethod(int access, String name, String desc,
    String signature, String[] exceptions) {
    MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
    if (mv == null) {
    return null;
    }
    return new MethodVisitor(ASM5, mv) {
    @Override public void visitMethodInsn(int opcode, String owner, String name,
    String desc, boolean itf) {
    if ("java/lang/Long".equals(owner) && "compare".equals(name)) {
    owner = "io/reactivex/internal/java/lang/Long";
    }
    super.visitMethodInsn(opcode, owner, name, desc, itf);
    }
    };
    }
    }

    View full-size slide

  112. $ javap -c build/classes/main/io/reactivex/internal/schedulers/TrampolineScheduler\$TimedRunnable.class
    Compiled from "TrampolineScheduler.java"
    final class io.reactivex.internal.schedulers.TrampolineScheduler$TimedRunnable implements
    java.lang.Comparable {
    final java.lang.Runnable run;
    final long execTime;
    public int compareTo(io.reactivex.internal.schedulers.TrampolineScheduler$TimedRunnable);
    Code:
    0: aload_0
    1: getfield #5 // Field execTime:J
    4: aload_1
    5: getfield #5 // Field execTime:J
    8: invokestatic #7 // Method io/reactivex/internal/java/lang/Long.compare:(JJ)I
    11: ireturn
    }

    View full-size slide

  113. $ javap -c build/classes/main/io/reactivex/internal/schedulers/TrampolineScheduler\$TimedRunnable.class
    Compiled from "TrampolineScheduler.java"
    final class io.reactivex.internal.schedulers.TrampolineScheduler$TimedRunnable implements
    java.lang.Comparable {
    final java.lang.Runnable run;
    final long execTime;
    public int compareTo(io.reactivex.internal.schedulers.TrampolineScheduler$TimedRunnable);
    Code:
    0: aload_0
    1: getfield #5 // Field execTime:J
    4: aload_1
    5: getfield #5 // Field execTime:J
    8: invokestatic #7 // Method io/reactivex/internal/java/lang/Long.compare:(JJ)I
    11: ireturn
    }

    View full-size slide

  114. public static void main(String... args) {
    sayHi(s -> System.out.println(s));
    }

    View full-size slide

  115. public static void main(String... args) {
    sayHi(s -> System.out.println(s));
    }
    BootstrapMethods:
    0: #27 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(
    Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;
    Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;
    Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/
    MethodType;)
    Ljava/lang/invoke/CallSite;
    Method arguments:
    #28 (Ljava/lang/String;)V
    #29 invokestatic Java8.lambda$main$0:(Ljava/lang/String;)V
    #28 (Ljava/lang/String;)V

    View full-size slide

  116. public static void main(String... args) {
    sayHi(s -> System.out.println(s));
    }
    [0002bc] Java8.main:([Ljava/lang/String;)V
    0000: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream;
    0003: new-instance v0, L-$$Lambda$1Osqr2Z9OSwjseX_0FMQJcCG_uM;
    0004: invoke-direct {v0, v1}, L-$$Lambda$1Osqr2Z9OSwjseX_0FMQJcCG_uM;.:
    (Ljava/io/PrintStream;)V
    0008: invoke-static {v0}, LJava8;.sayHi:(LJava8$Logger;)V

    View full-size slide

  117. Bytecode Transformer
    • Free to change, update, and delete any types, method, fields, and code

    View full-size slide

  118. Bytecode Transformer
    • Free to change, update, and delete any types, method, fields, and code
    • Create constructs which cannot be represented in source

    View full-size slide

  119. Bytecode Transformer
    • Free to change, update, and delete any types, method, fields, and code
    • Create constructs which cannot be represented in source
    • Use ASM or ByteBuddy, or some kind of IR

    View full-size slide

  120. Bytecode Transformer
    • GOOD: Unlimited power to create, modify, and delete anything as desired

    View full-size slide

  121. Bytecode Transformer
    • GOOD: Unlimited power to create, modify, and delete anything as desired
    • OKAY: Build system integration not always easy

    View full-size slide

  122. Bytecode Transformer
    • GOOD: Unlimited power to create, modify, and delete anything as desired
    • OKAY: Build system integration not always easy
    • OKAY: Can affect debugging and stacktraces in non-obvious ways

    View full-size slide

  123. Bytecode Transformer
    • GOOD: Unlimited power to create, modify, and delete anything as desired
    • OKAY: Build system integration not always easy
    • OKAY: Can affect debugging and stacktraces in non-obvious ways
    • MEH: Requires a lot of machinery and knowledge of bytecode

    View full-size slide

  124. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  125. Metaprogramming
    Runtime Compile-time
    Additive
    Mutating
    Reflection /
    Parser
    Source generator /
    Bytecode generator
    Source transformer /
    Bytecode transformer

    View full-size slide

  126. Network Requests

    View full-size slide

  127. GET /users HTTP/1.1
    [{id:1,name:"Jake"}]

    View full-size slide

  128. [{id:1,name:"Jake"}]
    GET /users HTTP/1.1

    View full-size slide

  129. [{id:1,name:"Jake"}]
    GET /users HTTP/1.1

    View full-size slide

  130. interface GitHubService {
    suspend fun users(): List
    }X
    data class User(
    val id: Long,
    val login: String
    )Y

    View full-size slide

  131. interface GitHubService {
    @GET("/users")
    suspend fun users(): List
    }X
    data class User(
    val id: Long,
    val login: String
    )Y

    View full-size slide

  132. interface GitHubService {
    @GET("/users")
    suspend fun users(): List
    }X
    @JsonClass
    data class User(
    val id: Long,
    val login: String
    )Y

    View full-size slide

  133. interface GitHubService {
    @GET("/users")
    suspend fun users(): List
    }X
    @JsonClass
    data class User(
    val id: Long,
    val login: String
    )Y

    View full-size slide

  134. interface GitHubService {
    @GET("/users")
    suspend fun users(): List
    }X
    @JsonClass
    data class User(
    val id: Long,
    val login: String
    )Y

    View full-size slide

  135. GET /users HTTP/1.1
    [{id:1,name:"Jake"}]

    View full-size slide

  136. GET /users HTTP/1.1
    [{id:1,name:"Jake"}]

    View full-size slide

  137. interface GitHubService {
    @GET("/users")
    suspend fun users(): List
    }X
    @JsonClass
    data class User(
    val id: Long,
    val login: String
    )Y

    View full-size slide

  138. interface GitHubService {
    @GET("/users")
    suspend fun users(): List
    }X
    @JsonClass
    data class User(
    val id: Long,
    val login: String
    )Y

    View full-size slide

  139. GET /users HTTP/1.1
    [{id:1,name:"Jake"}]
    interface GitHubService {
    @GET("/users")
    suspend fun users(): List
    }X
    @JsonClass
    data class User(
    val id: Long,
    val login: String
    )Y

    View full-size slide

  140. interface GitHubService {A
    @GET("/users")
    suspend fun users(): List
    }X

    View full-size slide

  141. class GitHubService(val client: OkHttpClient) {A
    suspend fun users(): List {
    // ...
    }B
    }X
    i
    n
    t
    e
    r
    f
    a
    c
    e
    @GET("/users")

    View full-size slide

  142. Android Layouts

    View full-size slide

  143. // search_item.xml
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    android:id="@+id/package_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/more_options"
    app:layout_constraintTop_toTopOf="parent"
    />
    android:id="@+id/class_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="@+id/package_name"
    app:layout_constraintEnd_toEndOf="@+id/package_name"
    app:layout_constraintTop_toBottomOf="@+id/package_name"
    app:layout_constraintBottom_toBottomOf="parent"
    />
    android:id="@+id/more_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_more_vert_black_24dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    />

    View full-size slide

  144. class SearchItemActivity extends Activity {
    private TextView packageNameView;
    private TextView classNameView;
    private ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    packageNameView = findViewById(R.id.package_name);
    classNameView = findViewById(R.id.class_name);
    moreOptionsView = findViewById(R.id.more_options);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }B
    }A

    View full-size slide

  145. private
    private
    private
    packageNameView = findViewById( );
    classNameView = findViewById( );
    moreOptionsView = findViewById( );
    class SearchItemActivity extends Activity {
    @BindView(R.id.package_name) TextView packageNameView;
    @BindView(R.id.class_name) TextView classNameView;
    @BindView(R.id.more_options) ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    ButterKnife.bind(this);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }B
    }A

    View full-size slide

  146. class SearchItemActivity : Activity {
    @Override fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.search_item)
    }B
    fun later() {
    packageName.text = "com.example"
    className.text = "ExampleClass"
    moreOptions.setImageResource(R.drawable.view_source)
    }B
    }A
    e
    x
    t
    e
    n
    d
    s
    @BindView(R.id.package_name) TextView packageNameView;
    @BindView(R.id.class_name) TextView classNameView;
    @BindView(R.id.more_options) ImageButton moreOptionsView;
    protected void
    ButterKnife.bind(this);
    void
    setText( );
    setText( );

    View full-size slide

  147. // search_item.xml
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    android:id="@+id/package_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/more_options"
    app:layout_constraintTop_toTopOf="parent"
    />
    android:id="@+id/class_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="@+id/package_name"
    app:layout_constraintEnd_toEndOf="@+id/package_name"
    app:layout_constraintTop_toBottomOf="@+id/package_name"
    app:layout_constraintBottom_toBottomOf="parent"
    />
    android:id="@+id/more_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_more_vert_black_24dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    />

    View full-size slide

  148. // search_item.xml
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    android:id="@+id/package_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/more_options"
    app:layout_constraintTop_toTopOf="parent"
    />
    android:id="@+id/class_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="@+id/package_name"
    app:layout_constraintEnd_toEndOf="@+id/package_name"
    app:layout_constraintTop_toBottomOf="@+id/package_name"
    app:layout_constraintBottom_toBottomOf="parent"
    />
    android:id="@+id/more_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_more_vert_black_24dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    />

    1. Layout file name

    View full-size slide

  149. // search_item.xml
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    android:id="@+id/package_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/more_options"
    app:layout_constraintTop_toTopOf="parent"
    />
    android:id="@+id/class_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="@+id/package_name"
    app:layout_constraintEnd_toEndOf="@+id/package_name"
    app:layout_constraintTop_toBottomOf="@+id/package_name"
    app:layout_constraintBottom_toBottomOf="parent"
    />
    android:id="@+id/more_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_more_vert_black_24dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    />

    1. Layout file name
    2. Root view type

    View full-size slide

  150. // search_item.xml
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    android:id="@+id/package_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/more_options"
    app:layout_constraintTop_toTopOf="parent"
    />
    android:id="@+id/class_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="@+id/package_name"
    app:layout_constraintEnd_toEndOf="@+id/package_name"
    app:layout_constraintTop_toBottomOf="@+id/package_name"
    app:layout_constraintBottom_toBottomOf="parent"
    />
    android:id="@+id/more_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_more_vert_black_24dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    />

    1. Layout file name
    2. Root view type
    3. Type of each view with ID

    View full-size slide

  151. // search_item.xml
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    android:id="@+id/package_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/more_options"
    app:layout_constraintTop_toTopOf="parent"
    />
    android:id="@+id/class_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="@+id/package_name"
    app:layout_constraintEnd_toEndOf="@+id/package_name"
    app:layout_constraintTop_toBottomOf="@+id/package_name"
    app:layout_constraintBottom_toBottomOf="parent"
    />
    android:id="@+id/more_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_more_vert_black_24dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    />

    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout

    View full-size slide

  152. // search_item.xml
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    android:id="@+id/package_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/more_options"
    app:layout_constraintTop_toTopOf="parent"
    />
    android:id="@+id/class_name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="@+id/package_name"
    app:layout_constraintEnd_toEndOf="@+id/package_name"
    app:layout_constraintTop_toBottomOf="@+id/package_name"
    app:layout_constraintBottom_toBottomOf="parent"
    />
    android:id="@+id/more_options"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_more_vert_black_24dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    />

    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  153. class SearchItemActivity extends Activity {
    private TextView packageNameView;
    private TextView classNameView;
    private ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    packageNameView = findViewById(R.id.package_name);
    classNameView = findViewById(R.id.class_name);
    moreOptionsView = findViewById(R.id.more_options);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  154. class SearchItemActivity extends Activity {
    private TextView packageNameView;
    private TextView classNameView;
    private ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    packageNameView = findViewById(R.id.package_name);
    classNameView = findViewById(R.id.class_name);
    moreOptionsView = findViewById(R.id.more_options);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  155. class SearchItemActivity extends Activity {
    private TextView packageNameView;
    private TextView classNameView;
    private ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    packageNameView = findViewById(R.id.package_name);
    classNameView = findViewById(R.id.class_name);
    moreOptionsView = findViewById(R.id.more_options);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration
    2. Root view type

    View full-size slide

  156. class SearchItemActivity extends Activity {
    private TextView packageNameView;
    private TextView classNameView;
    private ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    packageNameView = findViewById(R.id.package_name);
    classNameView = findViewById(R.id.class_name);
    moreOptionsView = findViewById(R.id.more_options);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration
    2. Root view type
    3. Type of each view with ID

    View full-size slide

  157. class SearchItemActivity extends Activity {
    private TextView packageNameView;
    private TextView classNameView;
    private ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    packageNameView = findViewById(R.id.package_name);
    classNameView = findViewById(R.id.class_name);
    moreOptionsView = findViewById(R.id.more_options);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    5. Whether an ID is present in

    every layout configuration
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout

    View full-size slide

  158. class SearchItemActivity extends Activity {
    private TextView packageNameView;
    private TextView classNameView;
    private ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    packageNameView = findViewById(R.id.package_name);
    classNameView = findViewById(R.id.class_name);
    moreOptionsView = findViewById(R.id.more_options);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  159. class SearchItemActivity extends Activity {
    @BindView(R.id.package_name) TextView packageNameView;
    @BindView(R.id.class_name) TextView classNameView;
    @BindView(R.id.more_options) ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    ButterKnife.bind(this);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  160. class SearchItemActivity extends Activity {
    @BindView(R.id.package_name) TextView packageNameView;
    @BindView(R.id.class_name) TextView classNameView;
    @BindView(R.id.more_options) ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    ButterKnife.bind(this);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  161. class SearchItemActivity extends Activity {
    @BindView(R.id.package_name) TextView packageNameView;
    @BindView(R.id.class_name) TextView classNameView;
    @BindView(R.id.more_options) ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    ButterKnife.bind(this);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  162. class SearchItemActivity extends Activity {
    @BindView(R.id.package_name) TextView packageNameView;
    @BindView(R.id.class_name) TextView classNameView;
    @BindView(R.id.more_options) ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    ButterKnife.bind(this);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  163. class SearchItemActivity extends Activity {
    @BindView(R.id.package_name) TextView packageNameView;
    @BindView(R.id.class_name) TextView classNameView;
    @BindView(R.id.more_options) ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    ButterKnife.bind(this);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  164. class SearchItemActivity extends Activity {
    @BindView(R.id.package_name) TextView packageNameView;
    @BindView(R.id.class_name) TextView classNameView;
    @BindView(R.id.more_options) ImageButton moreOptionsView;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search_item);
    ButterKnife.bind(this);
    }B
    void later() {
    packageName.setText("com.example");
    className.setText("ExampleClass");
    moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  165. class SearchItemActivity : Activity {
    @Override fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.search_item)
    }B
    fun later() {
    packageName.text = "com.example"
    className.text = "ExampleClass"
    moreOptions.setImageResource(R.drawable.view_source)
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  166. class SearchItemActivity : Activity {
    @Override fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.search_item)
    }B
    fun later() {
    packageName.text = "com.example"
    className.text = "ExampleClass"
    moreOptions.setImageResource(R.drawable.view_source)
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  167. class SearchItemActivity : Activity {
    @Override fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.search_item)
    }B
    fun later() {
    packageName.text = "com.example"
    className.text = "ExampleClass"
    moreOptions.setImageResource(R.drawable.view_source)
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  168. class SearchItemActivity : Activity {
    @Override fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.search_item)
    }B
    fun later() {
    packageName.text = "com.example"
    className.text = "ExampleClass"
    moreOptions.setImageResource(R.drawable.view_source)
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  169. class SearchItemActivity : Activity {
    @Override fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.search_item)
    }B
    fun later() {
    packageName.text = "com.example"
    className.text = "ExampleClass"
    moreOptions.setImageResource(R.drawable.view_source)
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration
    6. Kotlin-only

    View full-size slide

  170. class SearchItemActivity extends Activity {
    private SearchItemBinding layout;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    layout = setContentView(this, R.layout.search_item);
    }B
    void later() {
    layout.packageName.setText("com.example");
    layout.className.setText("ExampleClass");
    layout.moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration
    2. Root view type

    View full-size slide

  171. class SearchItemActivity extends Activity {
    private SearchItemBinding layout;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    layout = setContentView(this, R.layout.search_item);
    }B
    void later() {
    layout.packageName.setText("com.example");
    layout.className.setText("ExampleClass");
    layout.moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  172. // search_item.xml





    View full-size slide

  173. // search_item.xml







    View full-size slide

  174. // search_item.xml







    Android Gradle plugin

    View full-size slide

  175. // search_item.xml







    Android Gradle plugin
    class SearchItemBinding {
    public final TextView packageName;
    public final TextView className;
    public final ImageButton moreOptions;
    }A

    View full-size slide

  176. // search_item.xml







    Android Gradle plugin
    @Binding
    class SearchItemBinding {
    public final TextView packageName;
    public final TextView className;
    public final ImageButton moreOptions;
    }A

    View full-size slide

  177. // search_item.xml







    Android Gradle plugin
    annotationProcessor
    'androidx.databinding:databinding-compiler:3.4.0'
    @Binding
    class SearchItemBinding {
    public final TextView packageName;
    public final TextView className;
    public final ImageButton moreOptions;
    }A

    View full-size slide

  178. // search_item.xml







    Android Gradle plugin
    Java compiler
    annotationProcessor
    'androidx.databinding:databinding-compiler:3.4.0'
    @Binding
    class SearchItemBinding {
    public final TextView packageName;
    public final TextView className;
    public final ImageButton moreOptions;
    }A

    View full-size slide

  179. // search_item.xml







    Android Gradle plugin
    Java compiler
    annotationProcessor
    'androidx.databinding:databinding-compiler:3.4.0'
    @Binding
    class SearchItemBinding {
    public final TextView packageName;
    public final TextView className;
    public final ImageButton moreOptions;
    }A
    class SearchItemBindingImpl extends SearchItemBinding {
    // ...
    }A

    View full-size slide

  180. // search_item.xml







    Android Gradle plugin
    Java compiler
    annotationProcessor
    'androidx.databinding:databinding-compiler:3.4.0'
    @Binding
    class SearchItemBinding {
    public final TextView packageName;
    public final TextView className;
    public final ImageButton moreOptions;
    }A
    class SearchItemBindingImpl extends SearchItemBinding {
    // ...
    }A

    View full-size slide

  181. // search_item.xml







    Android Gradle plugin
    Java compiler
    annotationProcessor
    'androidx.databinding:databinding-compiler:3.4.0'
    @Binding
    class SearchItemBinding {
    public final TextView packageName;
    public final TextView className;
    public final ImageButton moreOptions;
    }A
    class SearchItemBindingImpl extends SearchItemBinding {
    // ...
    }A

    View full-size slide

  182. // search_item.xml







    Android Gradle plugin
    Java compiler
    annotationProcessor
    'androidx.databinding:databinding-compiler:3.4.0'
    @Binding
    class SearchItemBinding {
    public final TextView packageName;
    public final TextView className;
    public final ImageButton moreOptions;
    }A
    class SearchItemBindingImpl extends SearchItemBinding {
    // ...
    }A

    View full-size slide

  183. class SearchItemActivity extends Activity {
    private SearchItemBinding layout;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    layout = SearchItemBinding.inflate(this);
    setContentView(layout);
    }B
    void later() {
    layout.packageName.setText("com.example");
    layout.className.setText("ExampleClass");
    layout.moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    1. Layout file name
    2. Root view type
    3. Type of each view with ID
    4. Which IDs are contained

    inside each layout
    5. Whether an ID is present in

    every layout configuration

    View full-size slide

  184. class SearchItemActivity extends Activity {
    private SearchItemBinding layout;
    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    layout = SearchItemBinding.inflate(this);
    setContentView(layout);
    }B
    void later() {
    layout.packageName.setText("com.example");
    layout.className.setText("ExampleClass");
    layout.moreOptions.setImageResource(R.drawable.view_source);
    }
    }A
    (HINT)

    View full-size slide

  185. Database Queries

    View full-size slide

  186. CREATE TABLE user(
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT NOT NULL
    );
    SELECT email
    FROM user
    WHERE name LIKE 'Jake %'

    View full-size slide

  187. @Table("user")
    class User {
    @PrimaryKey @AutoIncrement
    long id;
    @NotNull
    String name;
    @NotNull
    String email;
    }A
    Database db = Database.create(context, User.class, ..);
    List jakes = db.from(User.class)
    .where("name", like("Jake %"))
    .select("email")
    .toList(String.class);

    View full-size slide

  188. Table
    @A Increment
    Database db = Database.create(context, User.class, ..);
    List jakes = db.from(User.class)
    .where("name", like("Jake %"))
    .select("email")
    .toList(String.class);
    @Entity(tableName = "user")
    class User {
    @PrimaryKey(autoGenerate = true)
    long id;
    @NotNull
    String name;
    @NotNull
    String email;
    }A
    @Dao
    interface UserDao {
    @Query("SELECT email FROM user WHERE name LIKE 'JAKE %'")
    List jakes();
    }H

    View full-size slide

  189. @Entity(tableName = "user")
    class User {
    @PrimaryKey(autoGenerate = true)
    long id;
    @NotNull
    String name;
    @NotNull
    String email;
    }A
    @Dao
    interface UserDao {
    @Query("SELECT email FROM user WHERE name LIKE 'JAKE %'")
    List jakes();
    }H

    View full-size slide

  190. @Entity(tableName = "user")
    class User {
    @PrimaryKey(autoGenerate = true)
    long id;
    @NotNull
    String name;
    @NotNull
    String email;
    }A
    @Dao
    interface UserDao {
    @Query("SELECT name, email FROM user WHERE name LIKE 'JAKE %'")
    List??> jakes();
    }H
    S
    t
    r
    i
    n
    g
    User

    View full-size slide

  191. @Entity(tableName = "user")
    class User {
    @PrimaryKey(autoGenerate = true)
    long id;
    @NotNullA
    String name;
    @NotNullB
    String email;
    }A
    @Dao
    interface UserDao {
    @Query("SELECT * FROM user WHERE name LIKE 'JAKE %'")
    List jakes();
    }H

    View full-size slide

  192. @Entity(tableName = "user")
    class User {
    @PrimaryKey(autoGenerate = true) long id;
    @NotNullAString name;
    @NotNullBString email;
    @Nullable LocalDate birthday;
    @NonNull LocalDate created;
    boolean is_admin;
    @Nullable String home_address;
    @Nullable String phone_number;
    }A
    @Dao
    interface UserDao {
    @Query("SELECT * FROM user WHERE name LIKE 'JAKE %'")
    List jakes();
    }H

    View full-size slide

  193. CREATE TABLE user(
    id INTEGER
    PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT NOT NULL
    );
    SELECT name, email
    FROM user
    WHERE name LIKE 'Jake %'

    View full-size slide

  194. CREATE TABLE user(
    id INTEGER
    PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT NOT NULL
    );
    SELECT name, email
    FROM user
    WHERE name LIKE 'Jake %'
    data class User(
    val id: Long,
    val name: String,
    val email: String
    )
    data class JakeResult(
    val name: String,
    val email: String
    )
    interface UserQueries {
    fun jakes(): Query
    }

    View full-size slide

  195. Mechanisms of
    Metaprogramming
    @JakeWharton

    View full-size slide