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
  2. Byte Code Weaving for Android and some libs to make

    it easy Mimic AfterBurner Morpheus lundi 22 septembre 14
  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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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(" + <id> + ");" + " ___BODY___ }") .doIt(); Add methods or insert byte code into existing methods. Byte Code Weaving for Android lundi 22 septembre 14
  28. 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
  29. 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
  30. Android Byte Code Weaving Remove boiler plate from our apps

    ! Just code what is worth coding lundi 22 septembre 14
  31. Thanks for your attention. Any questions ? Any comments ?

    Android Byte Code Weaving lundi 22 septembre 14