Share
Embed
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
Transcript
48 slides
-
Senior Android Developer Amadeus Software Labs Hello! I am Jitin
Sharma @js_2892 -
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 -
SAFETY AND IMMUTABILITY Nullability makes you rethink your life choices.
Immutability makes you appreciate final keyword in Java -
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 } -
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 -> { } } -
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 { } -
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) } } } -
EXTENSION FUNCTIONS Java Kotlin fun ImageView.loadImage(url: String) { Picasso.with(context) .load(url)
.into(this) } Helper.loadImage(banner, "https://"); cityImageView.loadImage(destination.imageUrl) -
SUPER-POWERED DATA CLASSES @Parcelize data class City(var name: String? =
null, var code: Int = 0) : Parcelable copy() equals() hashcode() newArray() writeToParcel() createFromParcel() describeContents() -
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 -
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 -
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. -
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 -
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 -
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 -
JVM ANNOTATIONS fun ImageView.loadImage(url: String) { Picasso.with(context) .load(url) .into(this) }
HelperKt.loadImage(banner, "https://"); Java Helper.kt -
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 -
JVM ANNOTATIONS @JvmName("stringToIntSafely") fun String.toIntSafely() { try { this.toInt() }
catch (_: Throwable) { } } Helper.stringToIntSafely(request.getId()); arguments?.getString("id")?.toIntSafely() Java Kotlin Helper.kt -
EXCEPTION HANDLING @Throws(Exception::class) fun exceptionalLogic() { } try { caller.exceptionalLogic();
} catch (Exception e) { e.printStackTrace(); } caller.exceptionalLogic() Java Kotlin Helper.kt -
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 -
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 :( -
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 :) -
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 -
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 -
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… -
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() } } -
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 -
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 -
JUnit testcases with Robolectric and Mockito Publish results to Sonarqube
dashboard Jenkins for CI Code in bitbucket HIGHER LEVEL WORKFLOW -
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 -
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 -
Baby Steps ~45000 250 lines of code Java classes 30
~5000 Kotlin classes lines of code -
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).