Slide 1

Slide 1 text

Introducing Kotlin in Android Project # B L R K O T L I N

Slide 2

Slide 2 text

Senior Android Developer Amadeus Software Labs Hello! I am Jitin Sharma @js_2892

Slide 3

Slide 3 text

About the App Uses a lot of open libraries from Timber to Play Services Completely written in Java Already in production with multiple customers and million plus downloads

Slide 4

Slide 4 text

Where we stood ~45000 255 lines of code java classes

Slide 5

Slide 5 text

WHY KOTLIN

Slide 6

Slide 6 text

Immutability Type/Null Safety Extensions Lambdas Interoperability

Slide 7

Slide 7 text

SAFETY AND IMMUTABILITY Nullability makes you rethink your life choices. Immutability makes you appreciate final keyword in Java

Slide 8

Slide 8 text

STANDARD LIBRARY supportActionBar?.apply { setDisplayHomeAsUpEnabled(true) setDisplayShowHomeEnabled(true) } if (getArguments() != null) { } arguments?.let{ }

Slide 9

Slide 9 text

SO MANY STANDARDS config?.decoderParams?.enabled = true config?.decoderParams?.redundancy = true config?.decoderParams?.length1 = 2 config?.decoderParams?.length2 = 16 config?.decoderParams?.apply { enabled = true redundancy = true length1 = 2 length2 = 16 } config?.decoderParams?.run { enabled = true redundancy = true length1 = 2 length2 = 16 } config?.decoderParams?.let { it.enabled = true it.redundancy = true it.length1 = 2 it.length2 = 16 }

Slide 10

Slide 10 text

STANDARD LIBRARY

Slide 11

Slide 11 text

https://medium.com/@elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84 HOW TO STANDARD.KT

Slide 12

Slide 12 text

SWITCH to WHEN Java Kotlin • Direct assignment to variable as an result of when closure if (fragment instanceof HomeFragment) { // Logic here } val view = when (viewType) { 1 -> layoutInflater.inflate(R.layout.item_pax_checkbox, parent, false) else -> layoutInflater.inflate(R.layout.item_pax_type, parent, false) } when(fragment) { is HomeFragment -> { } }

Slide 13

Slide 13 text

BYE BYE BUTTERKNIFE • Usage in ViewHolders - still experimental • Using override method onViewCreated instead of onCreateView in Fragments Java Kotlin @BindView(R.id.bottomLayout) LinearLayout bottomLayout; import kotlinx.android.synthetic.main.activity_main.* bottomLayout.forEachChild { }

Slide 14

Slide 14 text

EXTENSION FUNCTIONS fun View.show() { this.visibility = View.VISIBLE } fun Context.dpToPx(dp : Int): Float { val displayMetrics = this.resources.displayMetrics return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)).toFloat() } fun Drawable.withTint(colorInt: Int): Drawable { return with(this) { DrawableCompat.wrap(this).apply { DrawableCompat.setTint(this, colorInt) } } }

Slide 15

Slide 15 text

EXTENSION FUNCTIONS Java Kotlin fun ImageView.loadImage(url: String) { Picasso.with(context) .load(url) .into(this) } Helper.loadImage(banner, "https://"); cityImageView.loadImage(destination.imageUrl)

Slide 16

Slide 16 text

SUPER-POWERED DATA CLASSES @Parcelize data class City(var name: String? = null, var code: Int = 0) : Parcelable copy() equals() hashcode() newArray() writeToParcel() createFromParcel() describeContents()

Slide 17

Slide 17 text

VISIBILITY private public protected internal • By default, everything is public • All classes are declared final by default. Use open to make classes extendible. • Subclasses should be defined as inner to access variables of outer class(useful in ViewHolders) File level Module Level Everywhere Subclass level

Slide 18

Slide 18 text

S’MORE FEATURES typealias SessionData = ArrayList> tailrec fun HashMap.colorFinder(key: String): String? { val value = this[key] as String return if (value.startsWith("#")) { value } else { this.colorFinder(value) } } val appVersion: String get() = BuildConfig.VERSION_NAME

Slide 19

Slide 19 text

INTEROPERABILITY becomes a priority when inheriting a large java codebase

Slide 20

Slide 20 text

HANDLING INTEROP • In most cases Kotlin automatically takes care of interoperability. • void is converted to Unit when calling in Kotlin. • Empty getters and setters are converted to Properties. • Annotations in Java are respected by Kotlin to specify nullability of a certain variable.

Slide 21

Slide 21 text

FROM API 27, ANDROID ALSO HELPED @Nullable final public FragmentActivity getActivity() { return mHost == null ? null : (FragmentActivity) mHost.getActivity(); } Fragment.java(API 27) • context, activity and bundle with Nullable annotation are no longer platform types. • It’s always good to use Annotations on existing java code which could be used in Kotlin

Slide 22

Slide 22 text

JVM ANNOTATIONS class RequestCreator { companion object { fun buildUri(baseUrl: String): Uri { return Uri.parse(baseUrl).buildUpon().build() } } } RequestCreator.Companion.buildUri(“https://"); RequestCreator.buildUri("https://") Java Kotlin RequestCreator.kt

Slide 23

Slide 23 text

JVM ANNOTATIONS class RequestCreator { companion object { @JvmStatic fun buildUri(baseUrl: String): Uri { return Uri.parse(baseUrl).buildUpon().build() } } } RequestCreator.buildUri("https://"); RequestCreator.buildUri("https://") Java Kotlin RequestCreator.kt

Slide 24

Slide 24 text

JVM ANNOTATIONS fun ImageView.loadImage(url: String) { Picasso.with(context) .load(url) .into(this) } HelperKt.loadImage(banner, "https://"); Java Helper.kt

Slide 25

Slide 25 text

JVM ANNOTATIONS @file:JvmName("Helper") package ... import ... fun ImageView.loadImage(url: String) { Picasso.with(context) .load(url) .into(this) } Helper.loadImage(banner, "https://"); Java Helper.kt

Slide 26

Slide 26 text

JVM ANNOTATIONS @JvmName("stringToIntSafely") fun String.toIntSafely() { try { this.toInt() } catch (_: Throwable) { } } Helper.stringToIntSafely(request.getId()); arguments?.getString("id")?.toIntSafely() Java Kotlin Helper.kt

Slide 27

Slide 27 text

EXCEPTION HANDLING @Throws(Exception::class) fun exceptionalLogic() { } try { caller.exceptionalLogic(); } catch (Exception e) { e.printStackTrace(); } caller.exceptionalLogic() Java Kotlin Helper.kt

Slide 28

Slide 28 text

INTEROP WITH LAMBDAS class Caller { fun init(listener: (value: Int) -> Unit) { // Logic here } } caller.init { value: Int -> } Kotlin Caller.kt Java ??? No support for Lambda in Java 7

Slide 29

Slide 29 text

class Caller { fun init(listener: SelectionListener) { } } caller.init(object : SelectionListener { override fun selected(value: Int) { } }) Kotlin Caller.kt Java caller.init(new SelectionListener() { @Override public void selected(int value) { } }); interface SelectionListener { fun selected(value: Int) } SelectionListener.kt No SAM conversion for Kotlin interface :(

Slide 30

Slide 30 text

class Caller { fun init(listener: SelectionListener) { } } caller.init(SelectionListener { }) Kotlin Caller.kt Java caller.init(new SelectionListener() { @Override public void selected(int value) { } }); interface SelectionListener { void selected(int value); } SelectionListener.java Automatic SAM conversion for Java interface :)

Slide 31

Slide 31 text

WHAT ABOUT JAVA 8 April 2016 Support for Java 8 announced with Jack toolchain March 2017 Jack toolchain is deprecated April 2017 Java 8 support is merged into dx toolchain May 2017 Kotlin is announced as first class language for Android Nobody cares about Java 8 anymore

Slide 32

Slide 32 text

TRY JAVA 8?

Slide 33

Slide 33 text

INTEROP WITH JAVA 8 class Caller { fun init(listener: (value: Int) -> Unit) { // Logic here } } caller.init { value: Int -> } Kotlin Caller.kt Java caller.init(value -> { return Unit.INSTANCE; }); Return type necessary in Java lambdas

Slide 34

Slide 34 text

THAT MEANS… list.filter { it.startsWith("0") }.map { it.toInt() } Kotlin Java List filtered = filter(list, value -> value.startsWith("0")); List mapped = map(list, Integer::parseInt); Statically import methods from Kotlin Collection framework • Kotlin Utility methods could be directly used in your Java code. More idiomatic usage in Kotlin, but still…

Slide 35

Slide 35 text

THAT MEANS… Kotlin Java • Create your own Utils with higher order functions debugMode { StrictMode.enableDefaults() } debugMode(() -> { StrictMode.enableDefaults(); return Unit.INSTANCE; }); inline fun debugMode(block: () -> Unit) { if (BuildConfig.DEBUG) { block() } }

Slide 36

Slide 36 text

LINT AND CODE STYLE • Relying mostly on Android Studio lint checks for Kotlin. • Android now has a dedicated style guide and interop guide for Kotlin. • https://android.github.io/kotlin-guides/style.html • https://android.github.io/kotlin-guides/interop.html • https://github.com/shyiko/ktlint

Slide 37

Slide 37 text

AS 3.2 LINT UPDATES

Slide 38

Slide 38 text

BUSINESS AS USUAL

Slide 39

Slide 39 text

IMPACTS When things weren’t cool

Slide 40

Slide 40 text

BUILD

Slide 41

Slide 41 text

BUILD • CI server build time increased by 30-50%. • Kotlin’s compilation time is slower for clean builds. • However incremental compilation is faster than Java(enabled by default from 1.1.1). • Android Studio builds generally showed a difference of 10-20% as we moved from Gradle 2.14 -> 4.1, Kotlin 1.1.x -> 1.2.x

Slide 42

Slide 42 text

JUnit testcases with Robolectric and Mockito Publish results to Sonarqube dashboard Jenkins for CI Code in bitbucket HIGHER LEVEL WORKFLOW

Slide 43

Slide 43 text

TESTING AND QUALITY • Test cases were written in Kotlin. • Jacoco didn’t provide any coverage reports for Kotlin. • Sonarqube dashboard was affected due to unavailability of code coverage for Kotlin. When we started

Slide 44

Slide 44 text

TESTING AND QUALITY • Jacoco problem resolved by resolving correct class directories. • Sonarqube code coverage works fine as it uses Jacoco binary. • Jacoco has teeny tiny issues with generated methods. • Kotlin is still not supported by Sonarqube officially(third party plugins are present, though) • Quality checks and line-by-line coverage not available on Sonarqube Where we are now

Slide 45

Slide 45 text

Where we started ~45000 255 lines of code java classes

Slide 46

Slide 46 text

Baby Steps ~45000 250 lines of code Java classes 30 ~5000 Kotlin classes lines of code

Slide 47

Slide 47 text

WHAT WE ACHIEVED • Significantly lower rates of crashes in Kotlin files. • Better Kotlin education among developers. • Newer projects migrated to Kotlin completely. • Backend team also want a piece of Kotlin(probably got fed up of our bad java puns). • Human interaction with iOS Developers (Swift <=> Kotlin).

Slide 48

Slide 48 text

Thanks! twitter.com/@js_2892 Any questions?