From Java to Kotlin beyond Alt+Shift+Cmd+K Fabio Collini
@fabioCollini linkedin.com/in/fabiocollini github.com/fabioCollini medium.com/@fabioCollini codingjam.it Android programmazione avanzata Android Developers Italia Ego slide
Kotlin can be configured in one click
A Java class can be converted easily
ok, now what?
COLLE CTIONS ASYNC CODE DELE GATES Agenda COMPANION OBJECTS
COLLE CTIONS
for (Person person : people) { City city = person.getAddress().getCity(); if (city.getRegion().equals("Tuscany")) { cities.add(city.getName() + " (" + city.getCode() + ")"); }1 }2 System.out.println(b.toString()); List<Person> people = Arrays.asList(...); StringBuilder b = new StringBuilder(); for (String s : cities) { if (b.length() > 0) { b.append(", "); }3 b.append(s); }4 Set<String> cities = new TreeSet<>();
val people = listOf(...) val s = people .map { it.address.city } .filter { it.region == "Tuscany" } .distinct() .sortedBy { it.name } .joinToString { "${it.name} (${it.code})" } println(s)
String s = Stream.of(people) .map(it -> it.getAddress().getCity()) .filter(it -> it.getRegion().equals("Tuscany")) .distinct() .sortBy(City::getName) .map(it -> it.getName() + " (" + it.getCode() + ")") .collect(Collectors.joining(", ")); System.out.println(s);
val people = listOf(...) val s = people .map { it.address.city } .filter { it.region == "Tuscany" } .distinct() .sortedBy { it.name } .joinToString { "${it.name} (${it.code})" } println(s)
val people = listOf(...) val s = people .asSequence() .map { it.address.city } .filter { it.region == "Tuscany" } .map { "${it.name} (${it.code})" } .first() println(s)
val youngest = people.minBy { it.age } val peopleByCity: Map<City, List<Person>> = people.groupBy { it.address.city } val all: Boolean = people.all { it.address.city.name == "Florence" }1 val any: Boolean = people.any { it.address.city.name == "Florence" }2 val (adults: List<Person>, minors: List<Person>) = people.partition { it.age >= 18 }
val readOnlyList: List<Int> = listOf(10, 20, 30) val secondElement = readOnlyList[1]
val readOnlyList: List<Int> = listOf(10, 20, 30) val secondElement = readOnlyList[1] interface List<out E> : Collection<E> { //... operator fun get(index: Int): E //... }
val readOnlyList: List<Int> = listOf(10, 20, 30) val secondElement = readOnlyList[1] val mutableList: MutableList<Int> = mutableListOf(10, 20, 30) mutableList[1] = 21
val readOnlyList: List<Int> = listOf(10, 20, 30) val secondElement = readOnlyList[1] val mutableList: MutableList<Int> = mutableListOf(10, 20, 30) mutableList[1] = 21 interface MutableList<E> : List<E>, MutableCollection<E> { //... operator fun set(index: Int, element: E): E //... }
val readOnlyList: List<Int> = listOf(10, 20, 30) val secondElement = readOnlyList[1] val mutableList: MutableList<Int> = mutableListOf(10, 20, 30) mutableList[1] = 21 val map = mapOf(1 to "ABC", 2 to "DEF") infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
val readOnlyList: List<Int> = listOf(10, 20, 30) val secondElement = readOnlyList[1] val mutableList: MutableList<Int> = mutableListOf(10, 20, 30) mutableList[1] = 21 val map = mapOf(1 to "ABC", 2 to "DEF") val abc = map[1] interface Map<K, out V> { //... operator fun get(key: K): V? //... }
val readOnlyList: List<Int> = listOf(10, 20, 30) val secondElement = readOnlyList[1] val mutableList: MutableList<Int> = mutableListOf(10, 20, 30) mutableList[1] = 21 val map = mapOf(1 to "ABC", 2 to "DEF") val abc = map[1] val mutableMap = mutableMapOf(1 to "ABC", 2 to "DEF") mutableMap[3] = "XYZ" inline operator fun <K, V> MutableMap<K, V>.set( key: K, value: V): Unit { put(key, value) }
val otherList = (mutableList - readOnlyList) + mutableMap.keys public operator fun <T> Iterable<T>.minus( elements: Iterable<T>): List<T> { //... } public operator fun <T> Collection<T>.plus( elements: Iterable<T>): List<T> { //... }
COMPANION OBJECTS
private const val MY_PARAM = "my_param" class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object { fun newInstance(param: String): MyFragment { return MyFragment().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }9 }0
public class MyFragment extends Fragment { public static final Companion Companion = new Companion(); public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String myStringParam = getParam(); //... }9 public String getParam() { return getArguments().getString("my_param"); }8 public static final class Companion { public final MyFragment newInstance(String param) { MyFragment fragment = new MyFragment(); Bundle bundle = BundleKt.bundleOf( new Pair[] { TuplesKt.to("my_param", param) }1 ); fragment.setArguments(bundle); return fragment; }2 }3 }4 private const val MY_PARAM = "my_param" class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object { fun newInstance(param: String): MyFragment { return MyFragment().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }9 }0
private const val MY_PARAM = "my_param" class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object { @JvmStatic fun newInstance(param: String): MyFragment { return MyFragment().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }9 }0 public class MyFragment extends Fragment { public static final Companion Companion = new Companion(); public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String myStringParam = getParam(); //... }9 public String getParam() { return getArguments().getString("my_param"); }8 public static MyFragment newInstance(String param) { return Companion.newInstance(param); }x public static final class Companion { public final MyFragment newInstance(String param) { MyFragment fragment = new MyFragment(); Bundle bundle = BundleKt.bundleOf( new Pair[] { TuplesKt.to("my_param", param) }1 ); fragment.setArguments(bundle); return fragment; }2 }3 }4
private const val MY_PARAM = "my_param" class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object { fun newInstance(param: String): MyFragment { return MyFragment().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }9 }0
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object : FragmentCreator<String>(::MyFragment) {2 fun newInstance(param: String): MyFragment { return MyFragment().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }9 }0
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object : FragmentCreator<String>(::MyFragment) }0
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object : FragmentCreator<String>(::MyFragment) }0
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val param get() = arguments!!.get(MY_PARAM) as T }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 companion object : FragmentCreator<String>(::MyFragment) }0
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val Fragment.param get() = arguments!!.get(MY_PARAM) as T }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 companion object : FragmentCreator<String>(::MyFragment) }0
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val Fragment.param get() = arguments!!.get(MY_PARAM) as T }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 companion object : FragmentCreator<String>(::MyFragment) }0
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val Fragment.param get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T } }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 companion object : FragmentCreator<String>(::MyFragment) }0
class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 companion object : FragmentCreator<String>(::MyFragment) }0 val fragment = MyFragment.newInstance("ABC")
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val Fragment.param: T get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T } }1
fun bundleOf(vararg pairs: Pair<String, Any?>) = Bundle(pairs.size).apply { for ((key, value) in pairs) { when (value) { null -> putString(key, null) // Any nullable type will suffice. // Scalars is Boolean -> putBoolean(key, value) is Byte -> putByte(key, value) is Char -> putChar(key, value) is Double -> putDouble(key, value) is Float -> putFloat(key, value) is Int -> putInt(key, value) is Long -> putLong(key, value) is Short -> putShort(key, value) // References is Bundle -> putBundle(key, value) is CharSequence -> putCharSequence(key, value) is Parcelable -> putParcelable(key, value) // Scalar arrays is BooleanArray -> putBooleanArray(key, value) is ByteArray -> putByteArray(key, value) is CharArray -> putCharArray(key, value) is DoubleArray -> putDoubleArray(key, value) is FloatArray -> putFloatArray(key, value) is IntArray -> putIntArray(key, value) is LongArray -> putLongArray(key, value) is ShortArray -> putShortArray(key, value) // Reference arrays is Array<*> -> { val componentType = value::class.java.componentType @Suppress("UNCHECKED_CAST") // Checked by reflection. when { Parcelable::class.java.isAssignableFrom(componentType) -> { putParcelableArray(key, value as Array<Parcelable>) } String::class.java.isAssignableFrom(componentType) -> { putStringArray(key, value as Array<String>) } CharSequence::class.java.isAssignableFrom(componentType) -> { putCharSequenceArray(key, value as Array<CharSequence>) } Serializable::class.java.isAssignableFrom(componentType) -> { putSerializable(key, value) } else -> { val valueType = componentType.canonicalName throw IllegalArgumentException( "Illegal value array type $valueType for key "$key"") }5 }6 }7 // Last resort. Also we must check this after Array<*> as all arrays are serializable. is Serializable -> putSerializable(key, value) else -> { if (Build.VERSION.SDK_INT >= 18 && value is Binder) { putBinder(key, value) } else if (Build.VERSION.SDK_INT >= 21 && value is Size) { putSize(key, value) } else if (Build.VERSION.SDK_INT >= 21 && value is SizeF) { putSizeF(key, value) } else { val valueType = value.javaClass.canonicalName throw IllegalArgumentException("Illegal value type $valueType for key "$key"") }1 }2 }3 }4 }5
fun bundleOf(vararg pairs: Pair<String, Any?>) = Bundle(pairs.size).apply { for ((key, value) in pairs) { when (value) { null -> putString(key, null) // Any nullable type will suffice. // Scalars is Boolean -> putBoolean(key, value) is Byte -> putByte(key, value) is Char -> putChar(key, value) is Double -> putDouble(key, value) is Float -> putFloat(key, value) is Int -> putInt(key, value) is Long -> putLong(key, value) is Short -> putShort(key, value) // References is Bundle -> putBundle(key, value) is CharSequence -> putCharSequence(key, value) is Parcelable -> putParcelable(key, value) // Scalar arrays is BooleanArray -> putBooleanArray(key, value) is ByteArray -> putByteArray(key, value) is CharArray -> putCharArray(key, value) is DoubleArray -> putDoubleArray(key, value) is FloatArray -> putFloatArray(key, value) is IntArray -> putIntArray(key, value) is LongArray -> putLongArray(key, value)
} Serializable::class.java.isAssignableFrom(componentType) -> { putSerializable(key, value) } else -> { val valueType = componentType.canonicalName throw IllegalArgumentException( "Illegal value array type $valueType for key "$key"") }5 }6 }7 // Last resort. Also we must check this after Array<*> as all arrays are serializable. is Serializable -> putSerializable(key, value) else -> { if (Build.VERSION.SDK_INT >= 18 && value is Binder) { putBinder(key, value) } else if (Build.VERSION.SDK_INT >= 21 && value is Size) { putSize(key, value) } else if (Build.VERSION.SDK_INT >= 21 && value is SizeF) { putSizeF(key, value) } else { val valueType = value.javaClass.canonicalName throw IllegalArgumentException("Illegal value type $valueType for key "$key"") }1 }2 }3 }4 }5
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val Fragment.param: T get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T }2 }1
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { val Fragment.param: T get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T }2 }1 fun <T : Parcelable> FragmentCreator<T>.newInstance(param: T): Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { val Fragment.param: T get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T }2 }1 fun <T : Parcelable> FragmentCreator<T>.newInstance(param: T): Fragment { return factory().apply {4 arguments = Bundle().apply {5 putParcelable(MY_PARAM, param) }6 }7 }8
private const val MY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { val Fragment.param: T get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T }2 }1 fun <T : Parcelable> FragmentCreator<T>.newInstance(param: T): Fragment { return factory().apply {4 arguments = Bundle().apply {5 putParcelable(MY_PARAM, param) }6 }7 }8 fun FragmentCreator<String>.newInstance(param: String): Fragment { return factory().apply { arguments = Bundle().apply { putString(MY_PARAM, param) } } }
ASYNC CODE
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) myTextView.text = "Loading..." Thread.sleep(2000) myTextView.text = "Loading something else..." Thread.sleep(2000) myTextView.text = "Done" }_
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) myTextView.text = "Loading..." Thread.sleep(2000) myTextView.text = "Loading something else..." Thread.sleep(2000) myTextView.text = "Done" }_
/** * Delays coroutine for a given time without blocking * a thread and resumes it after a specified time. * ... */ suspend fun delay(time: Long, unit: TimeUnit = MILLISECONDS) { //... }
suspend
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) launch(UI) { myTextView.text = "Loading..." delay(2000) myTextView.text = "Loading something else..." delay(2000) myTextView.text = "Done" }async }end
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) launch(UI) { myTextView.text = "Loading..." delay(2000) myTextView.text = "Loading something else..." delay(2000) myTextView.text = "Done" }async }end
class MyService { fun loadData(): String { //... return slowMethod() }1 private fun slowMethod(): String { //... }2 }3
class MyService { suspend fun loadData(): String { return withContext(CommonPool) { //... slowMethod() }5 }1 private fun slowMethod(): String { //... }2 }3
class MyViewModel( private val service: MyService ) : ViewModel() { private val job = Job() fun load() { launch(CommonPool + job) { try { val data = service.loadData() updateUi(data) } catch (e: Exception) { updateUi(e.message) }1 }2 }3 private suspend fun updateUi(s: String?) { withContext(UI) { //... }4 }5 override fun onCleared() { job.cancel() }6 }7
class MyViewModel( private val service: MyService ) : ViewModel() { private val job = Job() fun load() { launch(CommonPool + job) { try { val data = service.loadData() updateUi(data) } catch (e: Exception) { updateUi(e.message) }1 }2 }3 private suspend fun updateUi(s: String?) { withContext(UI) { //... }4 }5 override fun onCleared() { job.cancel() }6 }7
class MyViewModel( private val service: MyService ) : ViewModel() { private val job = Job() fun load() { launch(CommonPool + job) { try { val data = service.loadData() updateUi(data) } catch (e: Exception) { updateUi(e.message) }1 }2 }3 private suspend fun updateUi(s: String?) { withContext(UI) { //... }4 }5 override fun onCleared() { job.cancel() }6 }7
class MyViewModel( private val service: MyService ) : ViewModel() { private val job = Job() fun load() { launch(CommonPool + job) { try { val data = service.loadData() updateUi(data) } catch (e: Exception) { updateUi(e.message) }1 }2 }3 private suspend fun updateUi(s: String?) { withContext(UI) { //... }4 }5 override fun onCleared() { job.cancel() }6 }7
class MyViewModel( private val service: MyService ) : ViewModel() { private val job = Job() fun load() { launch(CommonPool + job) { try { val data = service.loadData() updateUi(data) } catch (e: Exception) { updateUi(e.message) }1 }2 }3 private suspend fun updateUi(s: String?) { withContext(UI) { //... }4 }5 override fun onCleared() { job.cancel() }6 }7
interface MyService { @GET("login") fun login(): Deferred<String> @GET("data") fun loadData(@Query(“token") token: String): Deferred<Data> }A
interface MyService { @GET("login") fun login(): Deferred<String> @GET("data") fun loadData(@Query(“token") token: String): Deferred<Data> }A launch(CommonPool) { try { val token = service.login().await() val data = service.loadData(token).await() showInUi(data) } catch (e: Exception) { showError(e) }__ }___
service.login() .flatMap { service.loadData(it) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { data -> showInUi(data) }, { showError(it) } )end RxJavaCoroutines launch(CommonPool) { try { val token = service.login().await() val data = service.loadData(token).await() showInUi(data) } catch (e: Exception) { showError(e) }__ }___
RxJava Coroutines OR
RxJava Coroutines AND
RxJava Coroutines-> RxJavaCoroutines -> rxCompletable, rxMaybe, rxSingle, rxObservable, rxFlowable CompletableSource.await, MaybeSource.await, MaybeSource.awaitOrDefault, MaybeSource.openSubscription, SingleSource.await, ObservableSource.awaitFirst, ObservableSource.awaitFirstOrDefault, ObservableSource.awaitFirstOrElse, ObservableSource.awaitFirstOrNull, ObservableSource.awaitLast, ObservableSource.awaitSingle, ObservableSource.openSubscription, ObservableSource.iterator github.com/Kotlin/kotlinx.coroutines/tree/master/reactive/kotlinx-coroutines-rx2
launch(CommonPool) { try { val token = service.login().await() val data = service.loadData(token).await() showInUi(data)1 } catch (e: Exception) { showError(e) }__ }___
launch(CommonPool) { try { val token = service.login().await() val data = service.loadData(token).await() showInUi(data)1 } catch (e: Exception) { showError(e) }__ }___
launch(CommonPool) { try { val token = service.login().await() val data = service.loadData(token).await() val otherData = service.loadOtherData(token).await() showInUi(data, otherData)1 } catch (e: Exception) { showError(e) }__ }___
launch(CommonPool) { try { val token = service.login().await() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await())1 } catch (e: Exception) { showError(e) }__ }___
launch(CommonPool) { try { val token = service.login().await() updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) } catch (e: Exception) { showError(e) }__ }___
launch(CommonPool) { try { withTimeout(10, SECONDS) { val token = service.login().await() updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___
launch(CommonPool) { try { withTimeout(10, SECONDS) { val token = retry(3) { service.login().await() }T updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___
launch(CommonPool) { try { withTimeout(10, SECONDS) { val token = retry(3) { service.login().await() }T updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___ suspend fun <T> retry(times: Int, f: suspend () -> T): T { repeat(times - 1) { try { return f() } catch (ignored: Exception) { } } return f() }
launch(CommonPool) { try { withTimeout(10, SECONDS) { val token = exponentialBackoff(3) { service.login().await() }T updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___
launch(CommonPool) { try { withTimeout(10, SECONDS) { val token = exponentialBackoff(3) { service.login().await() }T updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___ suspend fun <T> exponentialBackoff(times: Int, f: suspend () -> T): T { var currentDelay = 100 + Random().nextInt(100) repeat(times - 1) { try { return f() } catch (e: Exception) { } delay(currentDelay) currentDelay *= 2 } return f() }
launch executes a task on a coroutine context an uncaught exception stops the app used to start a computation Deferred await returns the result or throws an exception can be used to execute something in parallel can be created using async withContext suspending method changes the execution thread useful to force the execution of a method on a thread
My 2 cents Suspending methods are easier to use than RxJava Singles Observables/Flowables are the best abstraction for a stream of data
DELE GATES
class TokenHolder {0 var token = "" }1
class TokenHolder {0 var token = "" }1 public class TokenHolder { private String token = ""; public String getToken() { return this.token; }1 public void setToken(String token) { this.token = token; }2 }3
class TokenHolder {0 var token = "" }1
class TokenHolder(private val prefs: SharedPreferences) {0 var token = "" }1
class TokenHolder(private val prefs: SharedPreferences) { var token get() = prefs.getString("token", "") set(value) = prefs.edit().putString("token", value).apply() }1
class TokenHolder(private val prefs: SharedPreferences) { var token get() = prefs.getString("token", "") set(value) = prefs.edit().putString("token", value).apply() }1 public class TokenHolder { private final SharedPreferences prefs; public TokenHolder(SharedPreferences prefs) { this.prefs = prefs; }1 public final String getToken() { return this.prefs.getString("token", ""); }2 public final void setToken(String value) { this.prefs.edit().putString("token", value).apply(); }3 }4
class TokenHolder(private val prefs: SharedPreferences) { var token get() = prefs.getString("token", "") set(value) = prefs.edit().putString("token", value).apply() }1
class TokenHolder(prefs: SharedPreferences) { var token by prefs.string() }1
class TokenHolder(prefs: SharedPreferences) { var token by prefs.string() }1 fun SharedPreferences.string(): ReadWriteProperty<Any, String> { return object : ReadWriteProperty<Any, String> { override fun getValue(thisRef: Any, property: KProperty<*>): String { return getString(property.name, "") }2 override fun setValue(thisRef: Any, property: KProperty<*>, value: String) { edit().putString(property.name, value).apply() }3 }4 }5
fun SharedPreferences.string( defaultValue: String = "" ): ReadWriteProperty<Any, String> { return object : ReadWriteProperty<Any, String> { override fun getValue(thisRef: Any, property: KProperty<*>): String { return getString(property.name, defaultValue) }2 override fun setValue(thisRef: Any, property: KProperty<*>, value: String) { edit().putString(property.name, value).apply() }3 }4 }5 class TokenHolder(prefs: SharedPreferences) { var token by prefs.string() }1
fun SharedPreferences.string( defaultValue: String = "", key: String? = null ): ReadWriteProperty<Any, String> { return object : ReadWriteProperty<Any, String> { override fun getValue(thisRef: Any, property: KProperty<*>): String { return getString(key ?: property.name, defaultValue) }2 override fun setValue(thisRef: Any, property: KProperty<*>, value: String) { edit().putString(key ?: property.name, value).apply() }3 }4 }5 class TokenHolder(prefs: SharedPreferences) { var token by prefs.string() }1
class TokenHolder(prefs: SharedPreferences) { var token by prefs.string() }1 public class TokenHolder { static final KProperty delegatedProperty = //.. private final ReadWriteProperty token$delegate; public TokenHolder(SharedPreferences prefs) { this.token$delegate = PrefsKt.string(prefs, null, null); }1 public final String getToken() { return (String)token$delegate.getValue(this, delegatedProperty); }2 public final void setToken(String var1) { token$delegate.setValue(this, delegatedProperty, var1); }3 }4
class TokenHolder(prefs: SharedPreferences) { var token by prefs.string() }1 tokenHolder.token += "ABC" prefs.edit().putString( "token", prefs.getString("token", "") + "ABC" ).apply()
Wrapping up Alt+Shift+Cmd+K is just the beginning functional code is natural in Kotlin code can be simplified using companion objects, coroutines and delegates
Links Demo Project github.com/fabioCollini/ArchitectureComponentsDemo Libraries Lightweight Stream github.com/aNNiMON/Lightweight-Stream-API Guides and posts Guide to kotlinx.coroutines by example github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md Async code using Kotlin Coroutines proandroiddev.com/async-code-using-kotlin-coroutines-233d201099ff Kotlin delegates in Android development part 1 hackernoon.com/kotlin-delegates-in-android-development-part-1-50346cf4aed7 Kotlin delegates in Android development part 2 proandroiddev.com/kotlin-delegates-in-android-development-part-2-2c15c11ff438
THANKS FOR YOUR ATTENTION
QUESTIONS? Android Developers Italia androiddevs.it

From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan

  • 1.
    From Java toKotlin beyond Alt+Shift+Cmd+K Fabio Collini
  • 2.
  • 3.
    Kotlin can beconfigured in one click
  • 4.
    A Java classcan be converted easily
  • 5.
  • 6.
  • 7.
  • 8.
    for (Person person: people) { City city = person.getAddress().getCity(); if (city.getRegion().equals("Tuscany")) { cities.add(city.getName() + " (" + city.getCode() + ")"); }1 }2 System.out.println(b.toString()); List<Person> people = Arrays.asList(...); StringBuilder b = new StringBuilder(); for (String s : cities) { if (b.length() > 0) { b.append(", "); }3 b.append(s); }4 Set<String> cities = new TreeSet<>();
  • 9.
    val people =listOf(...) val s = people .map { it.address.city } .filter { it.region == "Tuscany" } .distinct() .sortedBy { it.name } .joinToString { "${it.name} (${it.code})" } println(s)
  • 11.
    String s =Stream.of(people) .map(it -> it.getAddress().getCity()) .filter(it -> it.getRegion().equals("Tuscany")) .distinct() .sortBy(City::getName) .map(it -> it.getName() + " (" + it.getCode() + ")") .collect(Collectors.joining(", ")); System.out.println(s);
  • 12.
    val people =listOf(...) val s = people .map { it.address.city } .filter { it.region == "Tuscany" } .distinct() .sortedBy { it.name } .joinToString { "${it.name} (${it.code})" } println(s)
  • 13.
    val people =listOf(...) val s = people .asSequence() .map { it.address.city } .filter { it.region == "Tuscany" } .map { "${it.name} (${it.code})" } .first() println(s)
  • 14.
    val youngest =people.minBy { it.age } val peopleByCity: Map<City, List<Person>> = people.groupBy { it.address.city } val all: Boolean = people.all { it.address.city.name == "Florence" }1 val any: Boolean = people.any { it.address.city.name == "Florence" }2 val (adults: List<Person>, minors: List<Person>) = people.partition { it.age >= 18 }
  • 15.
    val readOnlyList: List<Int>= listOf(10, 20, 30) val secondElement = readOnlyList[1]
  • 16.
    val readOnlyList: List<Int>= listOf(10, 20, 30) val secondElement = readOnlyList[1] interface List<out E> : Collection<E> { //... operator fun get(index: Int): E //... }
  • 17.
    val readOnlyList: List<Int>= listOf(10, 20, 30) val secondElement = readOnlyList[1] val mutableList: MutableList<Int> = mutableListOf(10, 20, 30) mutableList[1] = 21
  • 18.
    val readOnlyList: List<Int>= listOf(10, 20, 30) val secondElement = readOnlyList[1] val mutableList: MutableList<Int> = mutableListOf(10, 20, 30) mutableList[1] = 21 interface MutableList<E> : List<E>, MutableCollection<E> { //... operator fun set(index: Int, element: E): E //... }
  • 19.
    val readOnlyList: List<Int>= listOf(10, 20, 30) val secondElement = readOnlyList[1] val mutableList: MutableList<Int> = mutableListOf(10, 20, 30) mutableList[1] = 21 val map = mapOf(1 to "ABC", 2 to "DEF") infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
  • 20.
    val readOnlyList: List<Int>= listOf(10, 20, 30) val secondElement = readOnlyList[1] val mutableList: MutableList<Int> = mutableListOf(10, 20, 30) mutableList[1] = 21 val map = mapOf(1 to "ABC", 2 to "DEF") val abc = map[1] interface Map<K, out V> { //... operator fun get(key: K): V? //... }
  • 21.
    val readOnlyList: List<Int>= listOf(10, 20, 30) val secondElement = readOnlyList[1] val mutableList: MutableList<Int> = mutableListOf(10, 20, 30) mutableList[1] = 21 val map = mapOf(1 to "ABC", 2 to "DEF") val abc = map[1] val mutableMap = mutableMapOf(1 to "ABC", 2 to "DEF") mutableMap[3] = "XYZ" inline operator fun <K, V> MutableMap<K, V>.set( key: K, value: V): Unit { put(key, value) }
  • 22.
    val otherList =(mutableList - readOnlyList) + mutableMap.keys public operator fun <T> Iterable<T>.minus( elements: Iterable<T>): List<T> { //... } public operator fun <T> Collection<T>.plus( elements: Iterable<T>): List<T> { //... }
  • 23.
  • 24.
    private const valMY_PARAM = "my_param" class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object { fun newInstance(param: String): MyFragment { return MyFragment().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }9 }0
  • 25.
    public class MyFragmentextends Fragment { public static final Companion Companion = new Companion(); public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String myStringParam = getParam(); //... }9 public String getParam() { return getArguments().getString("my_param"); }8 public static final class Companion { public final MyFragment newInstance(String param) { MyFragment fragment = new MyFragment(); Bundle bundle = BundleKt.bundleOf( new Pair[] { TuplesKt.to("my_param", param) }1 ); fragment.setArguments(bundle); return fragment; }2 }3 }4 private const val MY_PARAM = "my_param" class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object { fun newInstance(param: String): MyFragment { return MyFragment().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }9 }0
  • 26.
    private const valMY_PARAM = "my_param" class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object { @JvmStatic fun newInstance(param: String): MyFragment { return MyFragment().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }9 }0 public class MyFragment extends Fragment { public static final Companion Companion = new Companion(); public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String myStringParam = getParam(); //... }9 public String getParam() { return getArguments().getString("my_param"); }8 public static MyFragment newInstance(String param) { return Companion.newInstance(param); }x public static final class Companion { public final MyFragment newInstance(String param) { MyFragment fragment = new MyFragment(); Bundle bundle = BundleKt.bundleOf( new Pair[] { TuplesKt.to("my_param", param) }1 ); fragment.setArguments(bundle); return fragment; }2 }3 }4
  • 27.
    private const valMY_PARAM = "my_param" class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object { fun newInstance(param: String): MyFragment { return MyFragment().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }9 }0
  • 28.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object : FragmentCreator<String>(::MyFragment) {2 fun newInstance(param: String): MyFragment { return MyFragment().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }9 }0
  • 29.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object : FragmentCreator<String>(::MyFragment) }0
  • 30.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 val param get() = arguments!!.getString(MY_PARAM) companion object : FragmentCreator<String>(::MyFragment) }0
  • 31.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val param get() = arguments!!.get(MY_PARAM) as T }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 companion object : FragmentCreator<String>(::MyFragment) }0
  • 32.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val Fragment.param get() = arguments!!.get(MY_PARAM) as T }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 companion object : FragmentCreator<String>(::MyFragment) }0
  • 33.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val Fragment.param get() = arguments!!.get(MY_PARAM) as T }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 companion object : FragmentCreator<String>(::MyFragment) }0
  • 34.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val Fragment.param get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T } }1 class MyFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 companion object : FragmentCreator<String>(::MyFragment) }0
  • 35.
    class MyFragment :Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myStringParam = param //... }5 companion object : FragmentCreator<String>(::MyFragment) }0 val fragment = MyFragment.newInstance("ABC")
  • 36.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val Fragment.param: T get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T } }1
  • 37.
    fun bundleOf(vararg pairs:Pair<String, Any?>) = Bundle(pairs.size).apply { for ((key, value) in pairs) { when (value) { null -> putString(key, null) // Any nullable type will suffice. // Scalars is Boolean -> putBoolean(key, value) is Byte -> putByte(key, value) is Char -> putChar(key, value) is Double -> putDouble(key, value) is Float -> putFloat(key, value) is Int -> putInt(key, value) is Long -> putLong(key, value) is Short -> putShort(key, value) // References is Bundle -> putBundle(key, value) is CharSequence -> putCharSequence(key, value) is Parcelable -> putParcelable(key, value) // Scalar arrays is BooleanArray -> putBooleanArray(key, value) is ByteArray -> putByteArray(key, value) is CharArray -> putCharArray(key, value) is DoubleArray -> putDoubleArray(key, value) is FloatArray -> putFloatArray(key, value) is IntArray -> putIntArray(key, value) is LongArray -> putLongArray(key, value) is ShortArray -> putShortArray(key, value) // Reference arrays is Array<*> -> { val componentType = value::class.java.componentType @Suppress("UNCHECKED_CAST") // Checked by reflection. when { Parcelable::class.java.isAssignableFrom(componentType) -> { putParcelableArray(key, value as Array<Parcelable>) } String::class.java.isAssignableFrom(componentType) -> { putStringArray(key, value as Array<String>) } CharSequence::class.java.isAssignableFrom(componentType) -> { putCharSequenceArray(key, value as Array<CharSequence>) } Serializable::class.java.isAssignableFrom(componentType) -> { putSerializable(key, value) } else -> { val valueType = componentType.canonicalName throw IllegalArgumentException( "Illegal value array type $valueType for key "$key"") }5 }6 }7 // Last resort. Also we must check this after Array<*> as all arrays are serializable. is Serializable -> putSerializable(key, value) else -> { if (Build.VERSION.SDK_INT >= 18 && value is Binder) { putBinder(key, value) } else if (Build.VERSION.SDK_INT >= 21 && value is Size) { putSize(key, value) } else if (Build.VERSION.SDK_INT >= 21 && value is SizeF) { putSizeF(key, value) } else { val valueType = value.javaClass.canonicalName throw IllegalArgumentException("Illegal value type $valueType for key "$key"") }1 }2 }3 }4 }5
  • 38.
    fun bundleOf(vararg pairs:Pair<String, Any?>) = Bundle(pairs.size).apply { for ((key, value) in pairs) { when (value) { null -> putString(key, null) // Any nullable type will suffice. // Scalars is Boolean -> putBoolean(key, value) is Byte -> putByte(key, value) is Char -> putChar(key, value) is Double -> putDouble(key, value) is Float -> putFloat(key, value) is Int -> putInt(key, value) is Long -> putLong(key, value) is Short -> putShort(key, value) // References is Bundle -> putBundle(key, value) is CharSequence -> putCharSequence(key, value) is Parcelable -> putParcelable(key, value) // Scalar arrays is BooleanArray -> putBooleanArray(key, value) is ByteArray -> putByteArray(key, value) is CharArray -> putCharArray(key, value) is DoubleArray -> putDoubleArray(key, value) is FloatArray -> putFloatArray(key, value) is IntArray -> putIntArray(key, value) is LongArray -> putLongArray(key, value)
  • 39.
    } Serializable::class.java.isAssignableFrom(componentType) -> { putSerializable(key,value) } else -> { val valueType = componentType.canonicalName throw IllegalArgumentException( "Illegal value array type $valueType for key "$key"") }5 }6 }7 // Last resort. Also we must check this after Array<*> as all arrays are serializable. is Serializable -> putSerializable(key, value) else -> { if (Build.VERSION.SDK_INT >= 18 && value is Binder) { putBinder(key, value) } else if (Build.VERSION.SDK_INT >= 21 && value is Size) { putSize(key, value) } else if (Build.VERSION.SDK_INT >= 21 && value is SizeF) { putSizeF(key, value) } else { val valueType = value.javaClass.canonicalName throw IllegalArgumentException("Illegal value type $valueType for key "$key"") }1 }2 }3 }4 }5
  • 40.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { fun newInstance(param: T) : Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8 val Fragment.param: T get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T }2 }1
  • 41.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { val Fragment.param: T get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T }2 }1 fun <T : Parcelable> FragmentCreator<T>.newInstance(param: T): Fragment { return factory().apply { arguments = bundleOf(MY_PARAM to param) }7 }8
  • 42.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { val Fragment.param: T get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T }2 }1 fun <T : Parcelable> FragmentCreator<T>.newInstance(param: T): Fragment { return factory().apply {4 arguments = Bundle().apply {5 putParcelable(MY_PARAM, param) }6 }7 }8
  • 43.
    private const valMY_PARAM = "my_param" open class FragmentCreator<T>( val factory: () -> Fragment ) { val Fragment.param: T get() = arguments!!.get(MY_PARAM) as T fun param(fragment: Fragment): T { return fragment.arguments!!.get(MY_PARAM) as T }2 }1 fun <T : Parcelable> FragmentCreator<T>.newInstance(param: T): Fragment { return factory().apply {4 arguments = Bundle().apply {5 putParcelable(MY_PARAM, param) }6 }7 }8 fun FragmentCreator<String>.newInstance(param: String): Fragment { return factory().apply { arguments = Bundle().apply { putString(MY_PARAM, param) } } }
  • 44.
  • 45.
    override fun onCreate(savedInstanceState:Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) myTextView.text = "Loading..." Thread.sleep(2000) myTextView.text = "Loading something else..." Thread.sleep(2000) myTextView.text = "Done" }_
  • 46.
    override fun onCreate(savedInstanceState:Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) myTextView.text = "Loading..." Thread.sleep(2000) myTextView.text = "Loading something else..." Thread.sleep(2000) myTextView.text = "Done" }_
  • 47.
    /** * Delays coroutinefor a given time without blocking * a thread and resumes it after a specified time. * ... */ suspend fun delay(time: Long, unit: TimeUnit = MILLISECONDS) { //... }
  • 48.
  • 49.
    override fun onCreate(savedInstanceState:Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) launch(UI) { myTextView.text = "Loading..." delay(2000) myTextView.text = "Loading something else..." delay(2000) myTextView.text = "Done" }async }end
  • 50.
    override fun onCreate(savedInstanceState:Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val myTextView = findViewById<TextView>(R.id.text) launch(UI) { myTextView.text = "Loading..." delay(2000) myTextView.text = "Loading something else..." delay(2000) myTextView.text = "Done" }async }end
  • 51.
    class MyService { funloadData(): String { //... return slowMethod() }1 private fun slowMethod(): String { //... }2 }3
  • 52.
    class MyService { suspendfun loadData(): String { return withContext(CommonPool) { //... slowMethod() }5 }1 private fun slowMethod(): String { //... }2 }3
  • 53.
    class MyViewModel( private valservice: MyService ) : ViewModel() { private val job = Job() fun load() { launch(CommonPool + job) { try { val data = service.loadData() updateUi(data) } catch (e: Exception) { updateUi(e.message) }1 }2 }3 private suspend fun updateUi(s: String?) { withContext(UI) { //... }4 }5 override fun onCleared() { job.cancel() }6 }7
  • 54.
    class MyViewModel( private valservice: MyService ) : ViewModel() { private val job = Job() fun load() { launch(CommonPool + job) { try { val data = service.loadData() updateUi(data) } catch (e: Exception) { updateUi(e.message) }1 }2 }3 private suspend fun updateUi(s: String?) { withContext(UI) { //... }4 }5 override fun onCleared() { job.cancel() }6 }7
  • 55.
    class MyViewModel( private valservice: MyService ) : ViewModel() { private val job = Job() fun load() { launch(CommonPool + job) { try { val data = service.loadData() updateUi(data) } catch (e: Exception) { updateUi(e.message) }1 }2 }3 private suspend fun updateUi(s: String?) { withContext(UI) { //... }4 }5 override fun onCleared() { job.cancel() }6 }7
  • 56.
    class MyViewModel( private valservice: MyService ) : ViewModel() { private val job = Job() fun load() { launch(CommonPool + job) { try { val data = service.loadData() updateUi(data) } catch (e: Exception) { updateUi(e.message) }1 }2 }3 private suspend fun updateUi(s: String?) { withContext(UI) { //... }4 }5 override fun onCleared() { job.cancel() }6 }7
  • 57.
    class MyViewModel( private valservice: MyService ) : ViewModel() { private val job = Job() fun load() { launch(CommonPool + job) { try { val data = service.loadData() updateUi(data) } catch (e: Exception) { updateUi(e.message) }1 }2 }3 private suspend fun updateUi(s: String?) { withContext(UI) { //... }4 }5 override fun onCleared() { job.cancel() }6 }7
  • 58.
    interface MyService { @GET("login") funlogin(): Deferred<String> @GET("data") fun loadData(@Query(“token") token: String): Deferred<Data> }A
  • 59.
    interface MyService { @GET("login") funlogin(): Deferred<String> @GET("data") fun loadData(@Query(“token") token: String): Deferred<Data> }A launch(CommonPool) { try { val token = service.login().await() val data = service.loadData(token).await() showInUi(data) } catch (e: Exception) { showError(e) }__ }___
  • 60.
    service.login() .flatMap { service.loadData(it)} .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { data -> showInUi(data) }, { showError(it) } )end RxJavaCoroutines launch(CommonPool) { try { val token = service.login().await() val data = service.loadData(token).await() showInUi(data) } catch (e: Exception) { showError(e) }__ }___
  • 61.
  • 62.
  • 63.
    RxJava Coroutines-> RxJavaCoroutines -> rxCompletable,rxMaybe, rxSingle, rxObservable, rxFlowable CompletableSource.await, MaybeSource.await, MaybeSource.awaitOrDefault, MaybeSource.openSubscription, SingleSource.await, ObservableSource.awaitFirst, ObservableSource.awaitFirstOrDefault, ObservableSource.awaitFirstOrElse, ObservableSource.awaitFirstOrNull, ObservableSource.awaitLast, ObservableSource.awaitSingle, ObservableSource.openSubscription, ObservableSource.iterator github.com/Kotlin/kotlinx.coroutines/tree/master/reactive/kotlinx-coroutines-rx2
  • 64.
    launch(CommonPool) { try { valtoken = service.login().await() val data = service.loadData(token).await() showInUi(data)1 } catch (e: Exception) { showError(e) }__ }___
  • 65.
    launch(CommonPool) { try { valtoken = service.login().await() val data = service.loadData(token).await() showInUi(data)1 } catch (e: Exception) { showError(e) }__ }___
  • 66.
    launch(CommonPool) { try { valtoken = service.login().await() val data = service.loadData(token).await() val otherData = service.loadOtherData(token).await() showInUi(data, otherData)1 } catch (e: Exception) { showError(e) }__ }___
  • 67.
    launch(CommonPool) { try { valtoken = service.login().await() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await())1 } catch (e: Exception) { showError(e) }__ }___
  • 68.
    launch(CommonPool) { try { valtoken = service.login().await() updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) } catch (e: Exception) { showError(e) }__ }___
  • 69.
    launch(CommonPool) { try { withTimeout(10,SECONDS) { val token = service.login().await() updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___
  • 70.
    launch(CommonPool) { try { withTimeout(10,SECONDS) { val token = retry(3) { service.login().await() }T updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___
  • 71.
    launch(CommonPool) { try { withTimeout(10,SECONDS) { val token = retry(3) { service.login().await() }T updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___ suspend fun <T> retry(times: Int, f: suspend () -> T): T { repeat(times - 1) { try { return f() } catch (ignored: Exception) { } } return f() }
  • 72.
    launch(CommonPool) { try { withTimeout(10,SECONDS) { val token = exponentialBackoff(3) { service.login().await() }T updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___
  • 73.
    launch(CommonPool) { try { withTimeout(10,SECONDS) { val token = exponentialBackoff(3) { service.login().await() }T updateProgress() val data = service.loadData(token) val otherData = service.loadOtherData(token) showInUi(data.await(), otherData.await()) }B } catch (e: Exception) { showError(e) }__ }___ suspend fun <T> exponentialBackoff(times: Int, f: suspend () -> T): T { var currentDelay = 100 + Random().nextInt(100) repeat(times - 1) { try { return f() } catch (e: Exception) { } delay(currentDelay) currentDelay *= 2 } return f() }
  • 74.
    launch executes a taskon a coroutine context an uncaught exception stops the app used to start a computation Deferred await returns the result or throws an exception can be used to execute something in parallel can be created using async withContext suspending method changes the execution thread useful to force the execution of a method on a thread
  • 75.
    My 2 cents Suspendingmethods are easier to use than RxJava Singles Observables/Flowables are the best abstraction for a stream of data
  • 76.
  • 77.
  • 78.
    class TokenHolder {0 vartoken = "" }1 public class TokenHolder { private String token = ""; public String getToken() { return this.token; }1 public void setToken(String token) { this.token = token; }2 }3
  • 79.
  • 80.
    class TokenHolder(private valprefs: SharedPreferences) {0 var token = "" }1
  • 81.
    class TokenHolder(private valprefs: SharedPreferences) { var token get() = prefs.getString("token", "") set(value) = prefs.edit().putString("token", value).apply() }1
  • 82.
    class TokenHolder(private valprefs: SharedPreferences) { var token get() = prefs.getString("token", "") set(value) = prefs.edit().putString("token", value).apply() }1 public class TokenHolder { private final SharedPreferences prefs; public TokenHolder(SharedPreferences prefs) { this.prefs = prefs; }1 public final String getToken() { return this.prefs.getString("token", ""); }2 public final void setToken(String value) { this.prefs.edit().putString("token", value).apply(); }3 }4
  • 83.
    class TokenHolder(private valprefs: SharedPreferences) { var token get() = prefs.getString("token", "") set(value) = prefs.edit().putString("token", value).apply() }1
  • 84.
    class TokenHolder(prefs: SharedPreferences){ var token by prefs.string() }1
  • 85.
    class TokenHolder(prefs: SharedPreferences){ var token by prefs.string() }1 fun SharedPreferences.string(): ReadWriteProperty<Any, String> { return object : ReadWriteProperty<Any, String> { override fun getValue(thisRef: Any, property: KProperty<*>): String { return getString(property.name, "") }2 override fun setValue(thisRef: Any, property: KProperty<*>, value: String) { edit().putString(property.name, value).apply() }3 }4 }5
  • 86.
    fun SharedPreferences.string( defaultValue: String= "" ): ReadWriteProperty<Any, String> { return object : ReadWriteProperty<Any, String> { override fun getValue(thisRef: Any, property: KProperty<*>): String { return getString(property.name, defaultValue) }2 override fun setValue(thisRef: Any, property: KProperty<*>, value: String) { edit().putString(property.name, value).apply() }3 }4 }5 class TokenHolder(prefs: SharedPreferences) { var token by prefs.string() }1
  • 87.
    fun SharedPreferences.string( defaultValue: String= "", key: String? = null ): ReadWriteProperty<Any, String> { return object : ReadWriteProperty<Any, String> { override fun getValue(thisRef: Any, property: KProperty<*>): String { return getString(key ?: property.name, defaultValue) }2 override fun setValue(thisRef: Any, property: KProperty<*>, value: String) { edit().putString(key ?: property.name, value).apply() }3 }4 }5 class TokenHolder(prefs: SharedPreferences) { var token by prefs.string() }1
  • 88.
    class TokenHolder(prefs: SharedPreferences){ var token by prefs.string() }1 public class TokenHolder { static final KProperty delegatedProperty = //.. private final ReadWriteProperty token$delegate; public TokenHolder(SharedPreferences prefs) { this.token$delegate = PrefsKt.string(prefs, null, null); }1 public final String getToken() { return (String)token$delegate.getValue(this, delegatedProperty); }2 public final void setToken(String var1) { token$delegate.setValue(this, delegatedProperty, var1); }3 }4
  • 89.
    class TokenHolder(prefs: SharedPreferences){ var token by prefs.string() }1 tokenHolder.token += "ABC" prefs.edit().putString( "token", prefs.getString("token", "") + "ABC" ).apply()
  • 90.
    Wrapping up Alt+Shift+Cmd+K isjust the beginning functional code is natural in Kotlin code can be simplified using companion objects, coroutines and delegates
  • 91.
    Links Demo Project github.com/fabioCollini/ArchitectureComponentsDemo Libraries Lightweight Streamgithub.com/aNNiMON/Lightweight-Stream-API Guides and posts Guide to kotlinx.coroutines by example github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md Async code using Kotlin Coroutines proandroiddev.com/async-code-using-kotlin-coroutines-233d201099ff Kotlin delegates in Android development part 1 hackernoon.com/kotlin-delegates-in-android-development-part-1-50346cf4aed7 Kotlin delegates in Android development part 2 proandroiddev.com/kotlin-delegates-in-android-development-part-2-2c15c11ff438
  • 92.
  • 93.