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
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.
- 656