42
New Android build system Flavored with Roboguice and Robolectric , Ultracode Meetup, 2013-11-13 Andreas Würl Thomas Endres

[Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

  • Upload
    bemyapp

  • View
    921

  • Download
    0

Embed Size (px)

DESCRIPTION

By Thomas Endres & Andres Würl both Senior Consultant from TNG Technology Consulting https://www.tngtech.com Join the Ultracode Munich meetup: http://www.meetup.com/Ultracode-Munich/

Citation preview

Page 1: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

New Android build systemFlavored with Roboguice and Robolectric

, Ultracode Meetup, 2013-11-13Andreas Würl Thomas Endres

Page 2: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

OverviewA short introduction

New Android build systemRoboguice

Robolectric

Page 3: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

The speakersAndreas Würl is an IT consultant for TNG Technologyconsulting currently working in Unterföhring. In his freetime, he is contributing to the Blitzortung app available forAndroid and in development for iOS.

Thomas Endres is also an IT consultant for TNGTechnology consulting. In his free time, he is developingsoftware for controlling drones with bare hands, buildingapps and contributing to HTML5 frameworks.

Page 4: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Our apps

Blitzortung

Simple to use map based application visualizing real timelightning data provided by blitzortung.org. The currentthunderstorm situation at your fingertips.

Be Quiet - The noise alert

Whether you work in an office or in a class room, BeQuiet will help you reduce noise. When the volume is toohigh, it will blink and play a siren sound.

Page 5: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

OverviewA short introduction

New Android build systemRoboguice

Robolectric

Page 6: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Building Android applicationsOld school

Based on AntNo built-in dependency managementQuite inflexibleUsing old built-in library versionsNo support for real unit testsTest project needed for instrumentation tests

Page 7: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

The build xml file<target name="compile" depends="-resource-src, -aidl" description="Compiles project's .java files into .class files"> <!-- ... --> <javac encoding="ascii" target="1.5" debug="true" extdirs="" destdir="${out.classes.absolute.dir}" bootclasspathref="android.target.classpath" verbose="${verbose}" classpath="${extensible.classpath}" classpathref="android.libraries.jars"> <src path="${source.absolute.dir}" /> <src path="${gen.absolute.dir}" /> <src refid="android.libraries.src" /> <classpath> <fileset dir="${external.libs.absolute.dir}" includes="*.jar" /> <fileset dir="${extensible.libs.classpath}" includes="*.jar" /> </classpath> </javac></target>

Customization is very difficult

Page 8: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric
Page 9: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Building Android applicationsThe alternative

Based on Maven → Maven pluginAllows for dependency managementA lot more flexible→ But still far from being perfectReal unit tests are possibleStill using a test project for instrumentation tests

Page 10: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

The POM file<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.tngtech.internal.android</groupId> <artifactId>android-demo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>apk</packaging>

<dependencies> <dependency> <groupId>com.google.android</groupId> <artifactId>android</artifactId> <version>${platform.version}</version> <scope>provided</scope> </dependency> </dependencies>

<build> <plugins> <plugin> <groupId>com.jayway.maven.plugins.android.generation2</groupId> <artifactId>android-maven-plugin</artifactId> <version>3.7.0</version> <configuration> <androidManifestFile>${project.basedir}/AndroidManifest.xml</androidManifestFile> <assetsDirectory>${project.basedir}/assets</assetsDirectory> <resourceDirectory>${project.basedir}/res</resourceDirectory> <sdk><platform>18</platform></sdk> </configuration> </plugin> </plugins> </build></project>

Page 11: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Building Android applicationsThe new way

Based on Gradle → Gradle-PluginBuilt-in dependency managementUsing common Java patterns→ But flexible enough to change thatReal unit tests through pluginsInstrumentation tests within the same project

Page 12: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

The Gradle build filebuildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.6.+' }}

apply plugin: 'android'

repositories { mavenCentral()}

dependencies { // Put all dependencies here}

android { compileSdkVersion 18 buildToolsVersion "18.1.1"

defaultConfig { minSdkVersion 18 targetSdkVersion 18 }}

Page 13: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Android Studio

Page 14: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Android StudioThe facts

Based on IntelliJ

Ready to use

No additional plugins needed

Brings shortcuts for AVD and SDK manager

Out of the box support for the new build system

Possibility to migrate old projects

Page 15: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric
Page 16: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

OverviewA short introduction

New Android build systemRoboguice

Robolectric

Page 17: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

What the heck is Roboguice?A dependency injection containerAn implementation of JSR 330A fork of the Guice framework for the JDKEasy to configure and to use

Page 18: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Dependency injectionInstead of taking

be given

public class MainActivity extends Activity{ private LocationManager locationManager;

public void onCreate(Bundle savedInstance) { // ... locationManager = (LocationManager)

getSystemService(Activity.LOCATION_SERVICE);

}

}

public class MainActivity extends RoboActivity{ @Inject

private LocationManager locationManager;

public void onCreate(Bundle savedInstance) { // ... }

}

Page 19: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Principles of DIDon't let a class create objects on its ownInstead, pass them the objects they needThen you can exchange them for test purposesYou can pass in test doublesBut you can also exchange the "real" object easily

Loose coupling becomes a reality

Page 20: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

How can you inject objects?Through the constructor:

Into the field itself:

Into a property:

@Inject public MainActivity(LocationManager locationManager) { // ... this.locationManager = locationManager; }

@Inject private LocationManager locationManager;

@Inject public void setLocationManager(LocationManager locationManager) { this.locationManager = locationManager; }

Page 21: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

What can be injected?Arbitrary objects with a zero-arg constructor

Objects with a constructor managed by Roboguice

Views:

Resources:

A lot of standard Android objects:

LocationManager, AssetManager, ...

AlarmManager, NotificationManager, ...

Vibrator

@InjectView(R.id.specialButton) private Button button;

@InjectResource(R.drawable.specialPicture) private Drawable picture;

Page 22: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Robo* classesFor DI to work, you have to extend the robo classes:

Use them instead of the standard Android classes

RoboActivity instead of ActivityRoboListActivity instead of ListActivityRoboService instead of ServiceRoboFragment instead of Fragment...

Page 23: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Injecting providers:Sometimes, you need more than one object of a class

public class SomeObjectProvider implements Provider<SomeObject> {

@Inject

private SomeOtherObject someOtherObject;

@Override

public SomeObject get() {

return new SomeObject(someOtherObject);

}

}

private class SomeObjectUser {

@Inject

private Provider<SomeObject> someObjectProvider;

private SomeObject getObject() {

return someObjectProvider.get();

}

}

Page 24: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Injecting injectorsYou can also inject an injector

Then, you can get arbitrary objects out of the injector

@Inject

private Injector injector;

public <T> T giveMeAnObjectOf(Class<T> clazz) { return injector.getInstance(clazz); }

Page 25: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric
Page 26: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

ConfigurationBy defining a module, you can configure the objects injected

public class SomeModule extends AbstractModule { @Override

public void configure() { // Bind an interface to a specific class bind(SomeInterface.class).to(SomeImplementation.class);

// Bind a standard provider to the class bind(SomeClass.class).toProvider(SomeClassProvider.class);

}

}

Modules are discovered via "roboguice_modules.xml"<?xml version="1.0" encoding="utf-8"?><resources>

<string-array name="roboguice_modules">

<item>com.mypackage.SomeModule</item>

</string-array>

</resources>

Page 27: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Integrate Roboguice (1)Add roboguice to the compile dependencies:

Extend the Robo* classes in your objects:

Inject your dependencies:

// build.gradle dependencies {

// ... compile 'roboguice:roboguice:2.+'

}

public class SomeActivity extends RoboActivity { // ... }

@Inject

private SomeObject someObject;

Page 28: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Integrate Roboguice (2)Configure the module:

Register the module:

Write unit tests:

public class SomeModule extends AbstractModule { @Override

protected void configure() { bind(SomeClass.class).toProvider(SomeClassProvider.class);

// ...

}

}

<?xml version="1.0" encoding="utf-8"?><resources>

<string-array name="roboguice_modules">

<item>com.mypackage.SomeModule</item>

</string-array>

</resources>

public class SomeActivityTest { // How to do that?

}

Page 29: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

OverviewA short introduction

New Android build systemRoboguice

Robolectric

Page 30: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Android testingin new build system

Based on JUnit3Requires separate test projectRequires emulator or device for executionLacks real mockingBut initial support for some frameworks

Page 31: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric
Page 32: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Can I run tests locally?No. It's impossible!

Any method of the SDK will throw the followingexception when called:

java.lang.RuntimeException: Stub! at android.*

Why is that?

Android SDK jars for development only contain method stubs

Is there a solution?

Yes! Use Robolectric

Page 33: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

What the heck is Robolectric?Android SDK wrapper/enabler for local test executionJust another dependency of your projectSometimes dependency order is importantUses some magic to enable use of the stubbed SDK jarsUnfortunately not yet complete

Page 34: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

What do I get?Tests are running on the dev machineCurrent version of JUnit 4 is usedAny Mock- or Match-Framework can be usedCan be used in parallel with instrumentation tests

Page 35: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

How do I enable Robolectric?buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.6.+' classpath 'com.squareup.gradle:gradle-android-test-plugin:0.9.+' }}

apply plugin: 'android'apply plugin: 'android-test'...

Page 36: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

How do I implement a test?Just use the RobolectricTestRunner

@RunWith(RobolectricTestRunner.class) class SomeActivityTest { @Before public void setUp() { // Preparation for every test }

@Test public void testSomething() { // Your test code belongs here

assertThat(1, is(not(2)); } }

Page 37: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Basic conceptsShadows

TextView textView = (TextView) mainActivity.findViewById(R.id.helloWorld); final ShadowTextView shadowTextView = Robolectric.shadowOf(textView);

assertThat(shadowTextView.innerText(), is("Hello World!"));

Implementation in Robolectric @Implements(TextView.class) public class ShadowTextView extends ShadowView { @RealObject TextView realTextView;

@Override public String innerText() { CharSequence text = realTextView.getText(); return (text == null || realTextView.getVisibility() != View.VISIBLE) ? "" : text.toString(); }

@Implementation public void setPaintFlags(int paintFlags) { this.paintFlags = paintFlags; } }

Page 38: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Basic conceptsRobolectric builds up a full application contextActivities can be built

Testing resource access is possible as well

Modify preferences for tests

activity = Robolectric.buildActivity(MainActivity.class).create().get();

Resources resources = Robolectric.application.getResources(); assertThat(resources.getColor(R.color.Red), is(0xffff0000));

SharedPreferences defaultSharedPreferences = ShadowPreferenceManager.getDefaultSharedPreferences( Robolectric.application);

defaultSharedPreferences.edit() .putBoolean("test", true).putFloat("limit", 1.0f).apply();

Page 39: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

But ...Android Studio integration is not yet availableTests can be run via gradle task 'test'

IDE support only through ugly hacks

> gradle test

Page 40: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

SummaryThe new build system is a lot more flexible than the old oneAndroid Studio is a cool new tool for app developmentIt comes bundled with the SDK, you can start developmentimmediatelyBut there are still some issues with itRoboguice makes it possbible to decouple your applicationRobolectric can be used for local test execution

Page 41: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric
Page 42: [Ultracode Munich #4] Short introduction to the new Android build system including Android Studio, Roboguice and Robolectric

Thank you!Are there any questions?

, [email protected] [email protected]