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

Effective Android Development

Effective Android Development

A presentation of the techniques, tools & process that we employ during our daily work to build high-quality apps for our clients. Not a code-heavy, deep-down into our architecture kinda talk, but a high-level behind the scenes about our process to move from prototyping to design and to production. We want to stay nimble during development, and quickly iterate over features, which we like to accomplish through frequent releases, often paired with A/B testing. At the same time, we are convinced that building robust applications is helped with a Continuous Delivery setup where we highly value an automated test strategy. Working in an agile way while focusing on good architecture, testing and reporting took us some effort, and I’d like to explore how we run our projects trying to be as effective and collaborative as possible...
Presented during the Devoxx conference on Thursday, 10th November 2016.
http://cfp.devoxx.be/2016/talk/VOW-7651/Effective_Android_dev

Filip Maelbrancke

November 10, 2016
Tweet

More Decks by Filip Maelbrancke

Other Decks in Programming

Transcript

  1. Remote configuration - Check flags - maintenance - upgrade -

    Do specific work (load menu, …) - Check and act on feature switches Production API Test API Load Production config file https://xxxxx/2.1.json Load Test config file https://xxxxx/2.1-test.json
  2. –Martin Fowler Feature toggles are a powerful technique, allowing teams

    to modify system behavior without changing code.
  3. Key / value pairs @ Firebase Firebase Remote Config Change

    behaviour at runtime Developer console dynamic (no app update required) rules & conditions hosted, scalable, cross platform
  4. MODEL PRESENTER VIEW View Presenter View View Presenter Presenter Data

    Manager Interactors Prefs API API DB Network service Network service Preferences Helper Observables Observables Presentation Layer Domain Layer Data Layer STREAM Architecture
  5. MODEL PRESENTER VIEW View Presenter View View Presenter Presenter Data

    Manager Interactors Prefs API API DB Network service Network service Preferences Helper Observables Observables Presentation Layer Domain Layer Data Layer STREAM Architecture
  6. X

  7. Jetbrains Kotlin Good interoperability with Java Modern Small standard library

    (±600kb) Statically typed, no runtime overhead Reduce common errors Reduce boilerplate
  8. Data class public class User {
 
 private String firstName;


    private String lastName;
 private int age;
 
 public int getAge() {
 return age;
 }
 
 public void setAge(int age) {
 this.age = age;
 }
 
 public String getFirstName() {
 return firstName;
 }
 
 public void setFirstName(String firstName) {
 this.firstName = firstName;
 }
 
 public String getLastName() {
 return lastName;
 }
 
 public void setLastName(String lastName) {
 this.lastName = lastName;
 }
 
 @Override
 public boolean equals(Object o) {
 if (this == o) return true;
 if (o == null || getClass() != o.getClass()) return false;
 
 User user = (User) o;
 
 if (age != user.age) return false;
 if (firstName != null ? !firstName.equals(user.firstName) : user.firstName != null) {
 return false;
 }
 return lastName != null ? lastName.equals(user.lastName) : user.lastName == null;
 }
 
 @Override
 public int hashCode() {
 int result = firstName != null ? firstName.hashCode() : 0;
 result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
 result = 31 * result + age;
 return result;
 }
 
 @Override
 public String toString() {
 return "User{" +
 "age=" + age +
 ", firstName='" + firstName + '\'' +
 ", lastName='" + lastName + '\'' +
 '}';
 }
 } @AutoValue
 public abstract class User {
 
 public abstract String firstName();
 
 public abstract String lastName();
 
 public abstract int age();
 
 @AutoValue.Builder
 public abstract static class Builder {
 
 public abstract Builder firstName(String s);
 
 public abstract Builder lastName(String s);
 
 public abstract Builder age(int i);
 }
 
 public static Builder builder() {
 return new AutoParcel_User.Builder();
 }
 
 public abstract Builder toBuilder();
 }
  9. Data class public class User {
 
 private String firstName;


    private String lastName;
 private int age;
 
 public int getAge() {
 return age;
 }
 
 public void setAge(int age) {
 this.age = age;
 }
 
 public String getFirstName() {
 return firstName;
 }
 
 public void setFirstName(String firstName) {
 this.firstName = firstName;
 }
 
 public String getLastName() {
 return lastName;
 }
 
 public void setLastName(String lastName) {
 this.lastName = lastName;
 }
 
 @Override
 public boolean equals(Object o) {
 if (this == o) return true;
 if (o == null || getClass() != o.getClass()) return false;
 
 User user = (User) o;
 
 if (age != user.age) return false;
 if (firstName != null ? !firstName.equals(user.firstName) : user.firstName != null) {
 return false;
 }
 return lastName != null ? lastName.equals(user.lastName) : user.lastName == null;
 }
 
 @Override
 public int hashCode() {
 int result = firstName != null ? firstName.hashCode() : 0;
 result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
 result = 31 * result + age;
 return result;
 }
 
 @Override
 public String toString() {
 return "User{" +
 "age=" + age +
 ", firstName='" + firstName + '\'' +
 ", lastName='" + lastName + '\'' +
 '}';
 }
 } data class User(val firstName: String, val lastName: String, val age: Int) val user = User("Andy", "Rubin", 53) println(user) // User(firstName=Andy, lastName=Rubin, age=53) val usersBirthDay = user.copy(age = 54) // Named parameters
 println(usersBirthDay) // User(firstName=Andy, firstName=Rubin, age=54)
  10. Nullability var x: String? = "foo"
 
 x = null


    
 x.length // Does not compile 
 
 val y: String = null // Does not compile
  11. Nullability if (x != null) {
 x.length // Compiles
 }


    
 // Safe call
 val length = x?.length // Value null
 
 // Default value
 val length = if (x != null) x.length else -1 // Value -1
 
 // Elvis operator
 val length = x?.length ?: -1 // Value -1
  12. Kotlin • Properties • Data class • Function literal (Lambda)

    • Higher order functions • Extension functions • …
  13. Layout <android.support.design.widget.CoordinatorLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:fitsSystemWindows="true">
 
 <android.support.design.widget.AppBarLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content">
 


    <android.support.v7.widget.Toolbar
 android:id="@+id/toolbar"
 android:layout_width="match_parent"
 android:layout_height="?attr/actionBarSize"/>
 
 </android.support.design.widget.AppBarLayout>
 
 <com.bluelinelabs.conductor.ChangeHandlerFrameLayout
 android:id="@+id/container"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
 
 </android.support.design.widget.CoordinatorLayout>
  14. Layout Anko <android.support.design.widget.CoordinatorLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:fitsSystemWindows="true">
 
 <android.support.design.widget.AppBarLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content">


    
 <android.support.v7.widget.Toolbar
 android:id="@+id/toolbar"
 android:layout_width="match_parent"
 android:layout_height="?attr/actionBarSize"/>
 
 </android.support.design.widget.AppBarLayout>
 
 <com.bluelinelabs.conductor.ChangeHandlerFrameLayout
 android:id="@+id/container"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
 
 </android.support.design.widget.CoordinatorLayout> coordinatorLayout {
 fitsSystemWindows = true
 
 appBarLayout { 
 toolBar = toolbar {
 if (SDK_INT >= LOLLIPOP) elevation = 4f
 }.lparams(width = matchParent, height = actionBarSize()) }.lparams(width = matchParent)
 
 changeHandlerFrameLayout()
 .lparams(width = matchParent, height = matchParent) {
 behavior = ScrollingBehavior()
 } 
 }
  15. Anko performance test Specs ANKO XML Diff ALCATEL
 ONE TOUCH

    Mediatek MT6572
 Dual-core 1.3GHz Cortex-A7
 512MB RAM 169.33 ms 608.66 ms 359% HUAWEI Y300 Qualcomm MSM8225
 Dual-core 1.0 GHz Cortex-A5 512 MB RAM 593.66 ms 3435.33 ms 578% HUAWEI Y330 Mediatek MT6572 Dual-core 1.3 GHz Cortex-A7 512MB 162.33 ms 984 ms 606% Samsung Galaxy S2 Exynos 4210 Dual Dual-core 1.2 GHz Cortex-A9 1 GB RAM 207.33 ms 753.66 ms 363%
  16. Creating software = complex Continuous integration Ensure quality Automate high-quality,

    robust and reliable apps tedious / error-prone activities
  17. Continuous integration 1 2 3 4 CODE & COMMIT BUILD

    & CHECK CI PICKUP REPORT RESULTS
  18. Build pipeline Checkout / compile Unit tests Test coverage Code

    analysis Create deployable artifact Deploy for automatic QA test Trigger automated QA stage