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

Introducing Kotlin in an Android Project (BlrDr...

Jitin
April 21, 2018

Introducing Kotlin in an Android Project (BlrDroid Meetup, April 2018)

Kotlin is a great language addition to Android. In this presentation, we discuss details of how we introduced kotlin in a live project, tackled various challenges and finally put it in production.

Jitin

April 21, 2018
Tweet

More Decks by Jitin

Other Decks in Programming

Transcript

  1. 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
  2. SAFETY AND IMMUTABILITY Nullability makes you rethink your life choices.

    Immutability makes you appreciate final keyword in Java
  3. 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 }
  4. 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 -> { } }
  5. 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 { }
  6. 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) } } }
  7. EXTENSION FUNCTIONS Java Kotlin fun ImageView.loadImage(url: String) { Picasso.with(context) .load(url)

    .into(this) } Helper.loadImage(banner, "https://"); cityImageView.loadImage(destination.imageUrl)
  8. SUPER-POWERED DATA CLASSES @Parcelize data class City(var name: String? =

    null, var code: Int = 0) : Parcelable copy() equals() hashcode() newArray() writeToParcel() createFromParcel() describeContents()
  9. 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
  10. S’MORE FEATURES typealias SessionData = ArrayList<MutableMap<String, UserSession>> tailrec fun HashMap<String,String>.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
  11. 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.
  12. 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
  13. 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
  14. 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
  15. 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
  16. JVM ANNOTATIONS @JvmName("stringToIntSafely") fun String.toIntSafely() { try { this.toInt() }

    catch (_: Throwable) { } } Helper.stringToIntSafely(request.getId()); arguments?.getString("id")?.toIntSafely() Java Kotlin Helper.kt
  17. EXCEPTION HANDLING @Throws(Exception::class) fun exceptionalLogic() { } try { caller.exceptionalLogic();

    } catch (Exception e) { e.printStackTrace(); } caller.exceptionalLogic() Java Kotlin Helper.kt
  18. 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
  19. 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 :(
  20. 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 :)
  21. 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
  22. 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
  23. THAT MEANS… list.filter { it.startsWith("0") }.map { it.toInt() } Kotlin

    Java List<String> filtered = filter(list, value -> value.startsWith("0")); List<Integer> 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…
  24. 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() } }
  25. 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
  26. 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
  27. JUnit testcases with Robolectric and Mockito Publish results to Sonarqube

    dashboard Jenkins for CI Code in bitbucket HIGHER LEVEL WORKFLOW
  28. 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
  29. 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
  30. Baby Steps ~45000 250 lines of code Java classes 30

    ~5000 Kotlin classes lines of code
  31. 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).