Android development - Tips & Tricks

Hi!

linkedin.com/in/cosminstefan

cosmin@greenerpastures.ro

Goal for today?

Project Setup &
Build optimizations

Optimize Crashlytics
on debug builds

// build.gradle (app)
android {
  ...
  buildTypes {
   
    debug {
      ...
      firebaseCrashlytics {
      
        // If you don't need crash reporting for your debug build,
        // you can speed up your build by disabling mapping file uploading.
        mappingFileUploadEnabled false
      
      }
    }
  }
}

Optimize Crashlytics
on debug builds

// build.gradle (app)
android {
  ...
  buildTypes {
   
    debug {
      ...
      firebaseCrashlytics {
      
        // If you don't need crash reporting for your debug build,
        // you can speed up your build by disabling mapping file uploading.
        mappingFileUploadEnabled false
      
      }
    }
  }
}


<!-- AndroidManifest.xml -->
<meta-data android:name="firebase_crashlytics_collection_enabled"
           android:value="${firebaseCrashlyticsCollectionEnabled}" />

Optimize Crashlytics
on debug builds

// build.gradle (app)
android {
  ...
  buildTypes {
   
    debug {
      ...
      firebaseCrashlytics {
      
        // If you don't need crash reporting for your debug build,
        // you can speed up your build by disabling mapping file uploading.
        mappingFileUploadEnabled false
      
        // Or you can opt out of reporting completely
        // (Make sure you enable the flag on release builds)
        manifestPlaceholders.firebaseCrashlyticsCollectionEnabled = false
      }
    }
  }
}


<!-- AndroidManifest.xml -->
<meta-data android:name="firebase_crashlytics_collection_enabled"
           android:value="${firebaseCrashlyticsCollectionEnabled}" />

 Avoid compiling unnecessary resources

// build.gradle (app)
android {
  ...
  productFlavors {
    
    staging {
      ...
    
      // The following configuration limits the "staging" flavor to using
      // EN gresources and xxxhdpi screen-density resources.
      resConfigs "en", "xxxhdpi"
    }
    
    ...
  }
}

Profile your build

./gradlew clean

./gradlew --profile --offline --rerun-tasks assembleFlavorDebug

Dependencies versioning

// build.gradle (root)
ext {
  ...

  // Timber - https://github.com/JakeWharton/timber
  timber = "com.jakewharton.timber:timber:4.7.1"
  
  
  
}

Dependencies versioning

// build.gradle (root)
ext {
  ...

  // Timber - https://github.com/JakeWharton/timber
  timber = "com.jakewharton.timber:timber:4.7.1"
  
  // Retrofit - https://github.com/square/retrofit/releases
  retrofitVersion = '2.8.1'
  retrofit = [
    core: "com.squareup.retrofit2:retrofit:$retrofitVersion",
    gson: "com.squareup.retrofit2:converter-gson:$retrofitVersion"
  ]
  
  
}

Dependencies versioning

// build.gradle (root)
ext {
  ...

  // Timber - https://github.com/JakeWharton/timber
  timber = "com.jakewharton.timber:timber:4.7.1"
  
  // Retrofit - https://github.com/square/retrofit/releases
  retrofitVersion = '2.8.1'
  retrofit = [
    core: "com.squareup.retrofit2:retrofit:$retrofitVersion",
    gson: "com.squareup.retrofit2:converter-gson:$retrofitVersion"
  ]
  
  // AndroidX - https://developer.android.com/jetpack/androidx/releases/
  androidWorkManagerVersion = '2.3.4'
  androidx = [
    appcompat   : "androidx.appcompat:appcompat:1.2.0",
    fragment    : "androidx.fragment:fragment-ktx:1.2.5",
    workManager : [
      runtime   : "androidx.work:work-runtime:$androidWorkManagerVersion",
      rx        : "androidx.work:work-rxjava2:$androidWorkManagerVersion",
    ],
  ]  
}

Dependencies versioning

// build.gradle (app)
dependencies {
  ...

  // Timber
  implementation timber

  // Retrofit
  implementation retrofit.core
  implementation retrofit.gson

  // AndroidX
  implementation androidx.fragment
  implementation androidx.workManager.runtime
  implementation androidx.workManager.rx

}

Firebase BOM
versioning

// build.gradle (app)
dependencies {
  ...

  // Declare the dependencies for the desired Firebase products
  // by specifying versions
  implementation 'com.google.firebase:firebase-auth:19.4.0'
  implementation 'com.google.firebase:firebase-firestore-ktx:21.7.1'
  implementation 'com.google.firebase:firebase-storage-ktx:19.2.0'
}

Firebase BOM
versioning

// build.gradle (app)
dependencies {
  ...
  // Import the BoM for the Firebase platform
  implementation platform('com.google.firebase:firebase-bom:25.12.0')

  // Declare the dependencies for the desired Firebase products
  // without specifying versions
  implementation 'com.google.firebase:firebase-auth'
  implementation 'com.google.firebase:firebase-firestore-ktx'
}

Passwords & sensitive info

// build.gradle (app)
android {
  ...

  signingConfigs {
    release {
      // DON'T DO THIS!!
      storeFile file("myapp.keystore")
      storePassword "password123"
      keyAlias "thekey"
      keyPassword "password789"
    }
  }
}

Passwords & sensitive info

// gradle.properties
KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789
// build.gradle (app)
android {
  ...

  signingConfigs {
    release {
      try {
        storeFile file("myapp.keystore")
        storePassword KEYSTORE_PASSWORD
        keyAlias "thekey"
        keyPassword KEY_PASSWORD
      }
      catch (ex) {
        throw new InvalidUserDataException("Missing in gradle.properties")
      }
    }
  }
}

How about the code?

Mistakes are proof that you're trying!

Early mistake detection -
StrictMode

// Application.kt

fun onCreate() {

  if (BuildConfig.DEBUG) {
    StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
      .detectDiskReads()
      .detectDiskWrites()
      .detectNetwork() 
      // or .detectAll() for all detectable problems
      .penaltyLog()
      .build())
      
    StrictMode.setVmPolicy(VmPolicy.Builder()
      .detectLeakedSqlLiteObjects()
      .detectLeakedClosableObjects()
      .penaltyLog()
      .penaltyDeath()
      .build())
  }
  
  super.onCreate()
  
  ...
}

Life's too short to manually write Parcelable code

@Parcelize


@Parcelize
class Person(val name: String, val age: Int) : Parcelable

@Parcelize
enum class BookType: Parcelable { HISTORY, DRAMA, FICTION, BIO }

@Parcelize
class Book(val author: Person, val type: BookType) : Parcelable

sealed class Result : Parcelable {

  @Parcelize
  object Failure : Result

  @Parcelize
  object Error : Result

  @Parcelize
  class Success : Result
}
apply plugin: 'kotlin-android-extensions'

Be pragmatic about resources

Split style files
by semantics


<!-- styles.xml >
<resources>
  ...
  
  <style name="Widget.MyApp" parent="android:Widget" />

</resources>


<!-- styles-text.xml >
<resources>
  ...
  
  <!-- Base text appearance styles -->
  <style name="TextAppearance.MyApp" parent="TextAppearance.AppCompat" />
  
  <style name="TextAppearance.MyApp.Regular">
    <item name="android:fontFamily">@font/my_font_regular</item>
  </style>
  
</resources>

Use colors and dimens as palletes

<!-- colors.xml -->
<resources>
  ...

  <!-- DON'T do this -->  
  <color name="button_foreground">#FFFFFF</color>
  <color name="button_background">#2A91BD</color>


</resources>

Use colors and dimens as palletes

<!-- colors.xml -->
<resources>
  ...
  
  <!-- color pallete -->
  <color name="white">#FFFFFF</color>
  <color name="brand_blue">#3B92BC</color>
  

</resources>

Use colors and dimens as palletes

<!-- colors.xml -->
<resources>
  ...
  
  <!-- color pallete -->
  <color name="white">#FFFFFF</color>
  <color name="brand_blue">#3B92BC</color>

  <!-- specialised colors -->
  <color name="button_foreground">@color/white</color> 
  <color name="button_background">@color/brand_blue</color> 
  
</resources>
<!-- styles.xml -->
<resources>

  <style name="Widget.MyApp.ConfirmButton">
    <item name="android:foreground">@color/button_foreground</item>
    <item name="android:background">@color/button_background</item>
  </style>
</resources>

Use colors and dimens as palletes

<!-- dimens.xml -->
<resources>
  ...

  <!-- font sizes -->
  <dimen name="font_small">12sp</dimen>
  <dimen name="font_normal">15sp</dimen
  <dimen name="font_large">18sp</dimen>

  <!-- typical spacing between two views -->
  <dimen name="spacing_tiny">4dp</dimen>
  <dimen name="spacing_small">10dp</dimen>
  <dimen name="spacing_normal">14dp</dimen>
  <dimen name="spacing_large">24dp</dimen>

  <!-- typical sizes of views -->
  <dimen name="button_height_short">32dp</dimen>
  <dimen name="button_height_normal">40dp</dimen>
  <dimen name="button_height_tall">60dp</dimen>

</resources>

Theme != Style

Styles are a collection of view attributes
 

Themes are a collection of named resources, useful broadly across an app

Make use of attributes

<!-- themes.xml -->
<style name="Theme.MyApp" parent="…">
  <item name="colorPrimary">@color/teal_500</item>
  <item name="colorSecondary">@color/pink_200</item>
  <item name="android:windowBackground">@color/white</item>
</style>

Make use of attributes

<!-- themes.xml -->
<style name="Theme.MyApp" parent="…">
  <item name="colorPrimary">@color/teal_500</item>
  <item name="colorSecondary">@color/pink_200</item>
  <item name="android:windowBackground">@color/white</item>
</style>


<!-- my_layout.xml -->
<FrameLayout …
  android:background="?attr/colorSurface">
  
<TextView …
  android:textAppearance="?attr/textAppearanceHeadline5"

Android Developer's Toolbox

Memory Leak detection

HTTP & Throwables inspector

Casting of connected devices

Native rendering for After Effects animations

Graphical Assets generators

Android Developer's everyday toolbelt

Retrofit - type-safe HTTP client

Picasso - image loading & caching
Koin - lightweight dependency injection

Moshi - JSON parsing
Timber - simplified logging
Mockk - mocking for Kotlin

A few things to keep in mind

Thank you!

Tips and tricks for better Android development

By Cosmin Stefan

Tips and tricks for better Android development

With so many new tools & approaches coming up every month, it's hard to keep track of the latest and greatest ways of doing things in Android Development. During this talk, we'll explore some of the tools, patterns & tricks you can use to make Android development easier, faster and more fun.

  • 648