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

Byte Code Weaving for Android

Byte Code Weaving for Android

Discover how Byte Code weaving can completely eliminate boiler plate and make better libs and better apps.

Presented at Montreal Android User Group and Droid Con Paris (june & september 2014).

stephanenicolas

July 17, 2014
Tweet

More Decks by stephanenicolas

Other Decks in Programming

Transcript

  1. Byte Code Weaving
    for Android
    and some libs to make it easy
    Mimic AfterBurner Morpheus
    lundi 22 septembre 14

    View Slide

  2. Byte Code Weaving
    for Android
    and some libs to make it easy
    Mimic AfterBurner Morpheus
    lundi 22 septembre 14

    View Slide

  3. Stéphane NICOLAS
    snicolas
    stephanenicolas +stephane nicolas
    Android Senior Developer @ Groupon
    BoundBox
    And others : RoboDemo, RoboGuice,
    Quality Analysis Tools for Android,
    android-maven-plugin, etc..
    lundi 22 septembre 14

    View Slide

  4. 3
    5IF"OESPJE5FBNJT)JSJOH
    https://jobs.groupon.com/careers/
    lundi 22 septembre 14

    View Slide

  5. A simple example
    Byte Code Weaving for Android
    Goal : Log all life cycle methods of an Activity
    With LogLifeCycle, you just have to :
    Use the LogLifeCycle Gradle plugin inside your build.gradle file
    Then simply annotate every activity you wanna log :
    https://github.com/stephanenicolas/loglifecycle
    apply plugin: 'loglifecycle'
    @LogLifeCycle
    public class MainActivity extends Activity {
    ...
    }
    Activity
    build.gradle
    lundi 22 septembre 14

    View Slide

  6. LogLifeCycle
    Byte Code Weaving for Android
    > adb logcat | grep ⟳
    D/LogLifeCycle( 4640): MainActivity [1384162984] ⟳ onCreate
    D/LogLifeCycle( 4640): MainActivity [1384162984] ⟳ onApplyThemeResource
    D/LogLifeCycle( 4640): MainActivity [1384162984] ⟳ onWindowAttributesChanged
    D/LogLifeCycle( 4640): MainActivity [1384162984] ⟳ onWindowAttributesChanged
    D/LogLifeCycle( 4640): MainActivity [1384162984] ⟳ onWindowAttributesChanged
    D/LogLifeCycle( 4640): MainActivity [1384162984] ⟳ onContentChanged
    D/LogLifeCycle( 4640): MainActivity [1384162984] ⟳ onAttachFragment
    D/LogLifeCycle( 4640): MainActivity$MainFragment [1384183528] ⟳ onCreate
    D/LogLifeCycle( 4640): MainActivity [1384162984] ⟳ onStart
    D/LogLifeCycle( 4640): MainActivity$MainFragment [1384183528] ⟳ onStart
    D/LogLifeCycle( 4640): MainActivity [1384162984] ⟳ onTitleChanged
    D/LogLifeCycle( 4640): MainActivity [1384162984] ⟳ onPostCreate
    D/LogLifeCycle( 4640): MainActivity [1384162984] ⟳ onResume
    D/LogLifeCycle( 4640): MainActivity$MainFragment [1384183528] ⟳ onResume
    lundi 22 septembre 14

    View Slide

  7. Presentation plan
    Byte Code Weaving for Android
    I. Byte Code Weaving for Android
    1) Dynamic Vs. Static Byte Code Weaving
    2) Why Dynamic Byte Code weaving can’t work on Android
    3) Transforming classes during Android Post-Compilation build phase
    4) Javassist API : much simpler than annotation processing
    5) Gradle & Maven examples
    II. AfterBurner & Mimic : 2 libs to ease Byte Code Weaving on Android
    1) Mimic : Templating byte code injection
    2) Mimic : a simple use case
    3) AfterBurner : Fluent API for byte code injection
    4) AfterBurner : a simple use case
    lundi 22 septembre 14

    View Slide

  8. Byte Code Weaving for Android
    ‘‘ Byte Code Weaving is a technique for
    changing the byte code of compiled
    Java classes. ’’
    From oracle docs :
    docs.oracle.com/cd/E17904_01/doc.1111/e16596/bestpractice.htm
    Section 4.1 Changing Compiled Java Classes with Byte Code Weaving
    lundi 22 septembre 14

    View Slide

  9. 2 ways to weave Byte Code
    Byte Code Weaving for Android
    Dynamic Byte Code Weaving
    at Runtime
    Java classes are intercepted
    prior to being loaded by the
    class loader.
    Byte Code is modified at
    runtime.
    Static Byte Code Weaving
    at Post Compile Time
    Java classes Byte Code is
    m o d i f i e d r i g h t a f t e r
    compilation by javac.
    B y t e C o d e i s m o d i f i e d
    during build process.
    lundi 22 septembre 14

    View Slide

  10. Dynamic Weaving
    Byte Code Weaving for Android
    Java classes are intercepted prior to being loaded by
    the class loader.
    The interceptor is called a Java agent.
    Java agents are defined by a standard API :
    •java.lang.instrument.ClassTransformer
    •java.lang.instrument.Instrumentation
    •simple to define agents
    •no changes to build
    degrades performance
    lundi 22 septembre 14

    View Slide

  11. There are no java agents on
    Android Dalvik / ART !
    Byte Code Weaving for Android
    The closest thing to a java agent is an Instrumentation.
    From stackoverflow.com/q/12725279/693752
    lundi 22 septembre 14

    View Slide

  12. What is Java Instrumentation ?
    Byte Code Weaving for Android
    android.app.Instrumentation API :
    •void callActivityOnCreate (Activity activity, Bundle icicle)
    •void callActivityOnDestroy (Activity activity)
    •void callActivityOnPause (Activity activity)
    •void callActivityOnResume (Activity activity)
    •void callActivityOnStart (Activity activity)
    •void callActivityOnStop (Activity activity)
    •void callApplicationOnCreate (Application app)
    Android instrumentation is used to pilot application and
    activities, usually during testing (to provide mocked
    resources).
    lundi 22 septembre 14

    View Slide

  13. Limitations of Instrumentation
    Byte Code Weaving for Android
    The closest thing to a java agent is an Instrumentation.
    From stackoverflow.com/q/12725279/693752
    Instrumentation :
    •is Activity centric
    •can slow down apps easily
    •can alter behavior before or after a hook.
    i.e it can’t fill all your views inflated after
    setContentView() inside onCreate()
    setContentView() onCreate()
    It is far less powerful than a Java agent.
    lundi 22 septembre 14

    View Slide

  14. Byte Code Weaving for Android
    There are 2 options to weave Android Byte Code.
    1)after compilation by javac and before dexing
    2)after dexing
    Solution 2 requires that we find tools that can
    read/write Byte Code in the dex format.
    Solution 1 offers greater compatibility with existing
    Java Byte Code manipulation tools.
    Post Compilation on Android
    lundi 22 septembre 14

    View Slide

  15. Byte Code Weaving for Android
    Annotation
    processing
    Post
    Compilation
    Android Build phases
    lundi 22 septembre 14

    View Slide

  16. Static Weaving
    Byte Code Weaving for Android
    For Gradle :
    • Javassist gradle plugin
    github.com/darylteo/gradle-plugins
    For Maven :
    • Javassist maven plugin
    github.com/icon-Systemhaus-GmbH/javassist-maven-plugin
    A de facto standard library can be used : Javassist
    Highly depends on build tools.
    lundi 22 septembre 14

    View Slide

  17. Gradle & Maven
    Javassist plugins
    Byte Code Weaving for Android
    Both Javassist gradle & maven plugins share a simple
    interface to create class transformers :
    •void applyTransformations(CtClass classToTransform)
    •boolean shouldTransform(final CtClass candidateClass)
    developers can use them with
    the build tool of their choice.
    https://github.com/stephanenicolas/javassist-build-plugin-api
    lundi 22 septembre 14

    View Slide

  18. Javassist
    Byte Code Weaving for Android
    Javassist offers :
    a very simple API, quite close to Java standard
    reflection API
    it is FAR FAR simpler than annotation processing API
    de facto standard for Byte Code manipulation on JVM
    (used by Jboss, EasyMock, Mockito, JPA implementations, etc.)
    it works with everything that interprets Java Byte Code
    it cannot manipulate DEX files
    (see fork: github.com/crimsonwoods/javassist-android)
    lundi 22 septembre 14

    View Slide

  19. Byte Code Weaving for Android
    A ClassTransformer example
    https://github.com/stephanenicolas/afterburner
    cd to afterburner-sample-processor
    see ExampleProcessor
    (based on AfterBurner - optional)
    lundi 22 septembre 14

    View Slide

  20. Byte Code Weaving for Android
    Gradle Byte Code Weaving
    https://github.com/stephanenicolas/afterburner
    mvn clean install
    cd after-burner-sample
    gradle check1 -d
    gradle check2 -d
    lundi 22 septembre 14

    View Slide

  21. Byte Code Weaving for Android
    Maven Byte Code Weaving
    https://github.com/stephanenicolas/afterburner
    mvn clean install
    cd after-burner-sample
    mvn clean test
    lundi 22 septembre 14

    View Slide

  22. Mimic
    Templating Byte Code Injection
    class Template {
    public Template() {
    Log("Inside constr");
    }
    public void doStuff() {
    Log("Inside doStuff");
    }
    }
    class Ancestor {
    public void doStuff() {}
    }
    @Mimic(sourceClass=Template.class)
    class Example extends Ancestor {
    @Override
    public void doStuff() {
    super.doStuff();
    }
    }
    new Example.doStuff();
    //logs
    Inside constr
    Inside doStuff
    Byte Code Weaving for Android
    lundi 22 septembre 14

    View Slide

  23. Fine grain Mimic’ing
    enum MimicMode {
    /** Beginning of target method. */
    AT_BEGINNING,
    /** End of target method.*/
    BEFORE_RETURN,
    /** Before a call to an overridden method.*/
    BEFORE_SUPER,
    /** After a call to an overridden method.*/
    AFTER_SUPER,
    /** Instead of a call to an overridden method.*/
    REPLACE_SUPER,
    /** Before a call to a given method.*/
    BEFORE,
    /** After a call to a given method.*/
    AFTER;
    }
    Byte Code Weaving for Android
    lundi 22 septembre 14

    View Slide

  24. Fine grain Mimic’ing
    @Mimic(sourceClass=ExampleTemplate.class,
    mimicMethods={
    @MimicMethod(methodName="doOtherStuff",
    mode=MimicMode.AT_BEGINNING)
    })
    class Example extends ExampleAncestor {
    @Override
    void doStuff() {
    super.doStuff();
    }
    void doOtherStuff() {
    }
    }
    Byte Code Weaving for Android
    lundi 22 septembre 14

    View Slide

  25. Mimic : use case
    Activity
    Android
    Framework
    RoboActivity
    Library
    App MyRoboActivity
    RoboActivity is a complex class that provides nice features : 200 LOCs +.
    Byte Code Weaving for Android
    Templating Byte Code Injection
    lundi 22 septembre 14

    View Slide

  26. Mimic : use case
    Library
    App
    BUT all those classes have the exact same code as RoboActivity.
    The only difference is their super class.
    Byte Code Weaving for Android
    RoboActivity
    RoboFragmentActivity
    RoboTabActivity
    RoboTabActivity
    RoboSherlockActivity
    RoboActionBarActivity
    RoboListActivity
    RoboActivity
    RoboFragmentActivity
    RoboTabActivity
    RoboTabActivity
    RoboSherlockActivity
    RoboActionBarActivity
    RoboListActivity
    Templating Byte Code Injection
    lundi 22 septembre 14

    View Slide

  27. Mimic : use case
    Byte Code Weaving for Android
    Templating Byte Code Injection
    class RoboActivity extends Activity {
    void onCreate(Bundle b) {...}
    void onXXX() {...}
    }
    @RoboActivity
    class Example extends WhatEverActivity {
    @Override
    void onCreate(Bundle b) {
    super.onCreate(b);
    }
    }
    Mimic provides an extensible mechanism to create powerful
    custom annotations for your libs.
    lundi 22 septembre 14

    View Slide

  28. AfterBurner
    Fluent Byte Code Injection
    public class MyActivity {
    @InjectView TextView textView;
    @InjectView Button button;
    void onCreate(Bundle iCycle) {
    super.onCreate(iCycle);
    setContentView(R.layout.mylayout);
    //view injection takes place HERE
    button.setOnClickListener({
    textView.setText("Clicked !");
    });
    }
    Byte Code Weaving for Android
    lundi 22 septembre 14

    View Slide

  29. AfterBurner
    Fluent Byte Code Injection
    builder = new InsertableMethodBuilder(afterBurner);
    builder
    .insertIntoClass(classToTransform)
    .inMethodIfExists("onCreate")
    .afterACallTo("setContentView")
    .withBody(createFindViewStatements(classToTransform))
    .elseCreateMethodIfNotExists(
    "public void onCreate(Bundle b) {" +
    " super.onCreate(b);" +
    " setContentView(" + + ");" +
    " ___BODY___ }")
    .doIt();
    Add methods or insert byte code into existing methods.
    Byte Code Weaving for Android
    lundi 22 septembre 14

    View Slide

  30. Android Specification Request
    class MyActivity extends Activity {
    @InjectView TextView textView;
    @InjectView Button button;
    protected void onCreate(Bundle iCycle) {
    super.onCreate(iCycle);
    setContentView(R.layout.mylayout);
    button.setOnClickListener({
    textView.setText("Clicked !");
    });
    }
    github://TODO
    Byte Code Weaving for Android
    lundi 22 septembre 14

    View Slide

  31. Open futures
    Some ideas that could also become ASRs :
    • ButterKnife/RoboGuice -> @InjectView
    • Dagger/RoboGuice -> @Inject
    • Otto/RoboGuice/EventBus -> @Observes
    • Memento/IcePick -> @Statefull
    • Hugo -> @Log
    • Wire/Jackson2/gson to generate (de)serialization
    code for Pojos
    • to be continued...
    Remove boiler plate from our apps !
    --> Just code what is worth coding <--
    Byte Code Weaving for Android
    lundi 22 septembre 14

    View Slide

  32. All those projects are based on Byte Code Weaving
    https://github.com/stephanenicolas/injects
    Byte Code Weaving for Android
    lundi 22 septembre 14

    View Slide

  33. Android
    Byte Code Weaving
    Remove boiler plate from our apps !
    Just code what is worth coding
    lundi 22 septembre 14

    View Slide

  34. Thanks for your attention.
    Any questions ?
    Any comments ?
    Android
    Byte Code Weaving
    lundi 22 septembre 14

    View Slide