Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Together. Learn.

Slide 3

Slide 3 text

Agenda • Kotlin Android Extensions • Anko • Coroutines

Slide 4

Slide 4 text

Android Kotlin Extensions

Slide 5

Slide 5 text

Kotlin Android Extensions (KAE) • Gradle Plugin • Replaces view injecting libraries • No more findViewsById() • Be careful

Slide 6

Slide 6 text

KAE Use apply plugin: ‘kotlin-android-extensions' // Part of Kotlin IDEA Plugin so no extra dependencies

Slide 7

Slide 7 text

KAE Use import kotlinx.android.synthetic.main.fragment_episodes.recyclerView override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) recyclerView.setHasFixedSize(true) recyclerView.layoutManager = LinearLayoutManager(context) }

Slide 8

Slide 8 text

KAE Magic? public View _$_findCachedViewById(int var1) { } private HashMap _$_findViewCache; if(this._$_findViewCache == null) { this._$_findViewCache = new HashMap(); } View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1)); if(var2 == null) { View var10000 = this.getView(); if(var10000 == null) { return null; } var2 = var10000.findViewById(var1); this._$_findViewCache.put(Integer.valueOf(var1), var2); } return var2;

Slide 9

Slide 9 text

KAE Decompilation public void onViewCreated(@Nullable View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); ((RecyclerView)this._$_findCachedViewById(id.recyclerView)).setHasFixedSize(true); ((RecyclerView)this._$_findCachedViewById(id.recyclerView)) .setLayoutManager((LayoutManager)(new LinearLayoutManager(this.getContext()))); } override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) recyclerView.setHasFixedSize(true) recyclerView.layoutManager = LinearLayoutManager(context) } // $FF: synthetic method public void onDestroyView() { super.onDestroyView(); this._$_clearFindViewByIdCache(); }

Slide 10

Slide 10 text

KAE ViewHolder class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(item: Item, listener: (Item) -> Unit) = with(itemView) { date.text = simpleDateFormatter.format(item.pubDate) name.text = item.title Picasso.with(context) .load(item.image!!.href) .placeholder(ContextCompat.getDrawable(context, R.drawable.placeholder)) .into(image) description.text = item.description duration.text = item.duration?.value ?: "" card.setOnClickListener { listener(item) } } }

Slide 11

Slide 11 text

KAE ViewHolder Decompilation public final void bind(@NotNull Item item, @NotNull Function1 listener) { Intrinsics.checkParameterIsNotNull(item, "item"); Intrinsics.checkParameterIsNotNull(listener, "listener"); View var3 = this.itemView; ((TextView)var3.findViewById(id.date)).setText(…); ((TextView)var3.findViewById(id.name)).setText((CharSequence)item.getTitle()); (….) ((CardView)var3.findViewById(id.card)).setOnClickListener(…); }

Slide 12

Slide 12 text

KAE ViewHolder The Right Way™ class ViewHolder(override val containerView: View?) : RecyclerView.ViewHolder(containerView), LayoutContainer { fun bind(item: Item, listener: (Item) -> Unit) = with(itemView) { date.text = simpleDateFormatter.format(item.pubDate) name.text = item.title Picasso.with(context) .load(item.image!!.href) .placeholder(ContextCompat.getDrawable(context, R.drawable.placeholder)) .into(image) description.text = item.description duration.text = item.duration?.value ?: "" card.setOnClickListener { listener(item) } } } public interface LayoutContainer { public val containerView: View? }

Slide 13

Slide 13 text

KAE ViewHolder The Right Way™ public View _$_findCachedViewById(int var1) { if(this._$_findViewCache == null) { this._$_findViewCache = new HashMap(); } View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1)); if(var2 == null) { View var10000 = this.getContainerView(); if(var10000 == null) { return null; } var2 = var10000.findViewById(var1); this._$_findViewCache.put(Integer.valueOf(var1), var2); } return var2; } private HashMap _$_findViewCache;

Slide 14

Slide 14 text

KAE ViewHolder The Right Way™ public final void bind(@NotNull Item item, @NotNull Function1 listener) { Intrinsics.checkParameterIsNotNull(item, "item"); Intrinsics.checkParameterIsNotNull(listener, "listener"); ((TextView)this._$_findCachedViewById(id.date)).setText(…); ((TextView)this._$_findCachedViewById(id.name)).setText((CharSequence)item.getTitle()); (..) ((CardView)this._$_findCachedViewById(id.card)).setOnClickListener(…); } // Same for Custom Views!

Slide 15

Slide 15 text

Anko

Slide 16

Slide 16 text

Anko • Anko Commons • Anko Layouts • Anko SQLite • Anko Coroutines

Slide 17

Slide 17 text

Anko Commons - Intents fun startActivity(context: Context) { val intent = Intent(context, MainActivity::class.java) intent.putExtra(EXTRA_STATE, "3") context.startActivity(intent) } fun startActivity(context: Context) { context.startActivity(EXTRA_STATE to 3) } fun startActivity(context: Context) = context.startActivity(EXTRA_STATE to 3) context.share("url to a new podcast!”)

Slide 18

Slide 18 text

Anko Commons - Dialogs context.alert("Hi, I'm Roy", "Have you tried turning it off and on again?") { yesButton { context.toast("Oh…") } noButton {} }.show()

Slide 19

Slide 19 text

Anko Commons - Logging class EpisodesPresenter(private val callback: WeakReference): AnkoLogger { fun someFunction(item: Item) { info("clicked on $item") warn("This is a warning") } }

Slide 20

Slide 20 text

Anko Commons - Misc // colors 0xff0000.opaque // non-transparent red 0x99.gray.opaque // non-transparent #999999 gray // Dimensions val dip = dip(20) // 60 val sp = sp(20) // 60 val px2dip = px2dip(100) // 33.333332

Slide 21

Slide 21 text

Anko Layouts - before override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater!!.inflate(R.layout.fragment_episodes, container, false) }

Slide 22

Slide 22 text

Anko Layouts - after override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return UI { verticalLayout { padding = dip(30) editText { hint = "Name" textSize = 24f } editText { hint = "Password" textSize = 24f } button("Login") { textSize = 26f } } }.view }

Slide 23

Slide 23 text

Anko SQLite class DatabaseOpenHelper(ctx: Context) : ManagedSQLiteOpenHelper(ctx, "MyDatabase", null, 1) { companion object { private var instance: DatabaseOpenHelper? = null @Synchronized fun getInstance(ctx: Context): DatabaseOpenHelper { if (instance == null) { instance = DatabaseOpenHelper(ctx.applicationContext) } return instance!! } } override fun onCreate(db: SQLiteDatabase) { // Here you create tables db.createTable("Customer", false, "id" to INTEGER, "name" to TEXT, "photo" to BLOB) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { // Here you can upgrade tables, as usual db.dropTable("Customer", true) } }

Slide 24

Slide 24 text

Anko Coroutines

Slide 25

Slide 25 text

Anko Coroutines - before object GetRssFeedRequest { fun getFeed(callback: Callback) { val retrofit = RetrofitUtils.getRetrofit() val service = retrofit.create(RssFeedService::class.java) val feed = service.getFeed() feed.enqueue(callback) } }

Slide 26

Slide 26 text

Anko Coroutines - before fun getFeed(): LiveData> { val data = MutableLiveData>() GetRssFeedRequest.getFeed(object : Callback { // retrofit override fun onResponse(call: Call?, response: Response?) { val rss = response?.body() if (rss != null) { data.value = Resource.success(rss) } } override fun onFailure(call: Call?, t: Throwable?) { Log.d(TAG, t?.localizedMessage) data.value = Resource.error() } }) return data }

Slide 27

Slide 27 text

Anko Coroutines - after object GetRssFeedRequest { fun getFeed(): Response? { val retrofit = RetrofitUtils.getRetrofit() val service = retrofit.create(RssFeedService::class.java) val feed = service.getFeed() return feed.execute() } }

Slide 28

Slide 28 text

Anko Coroutines - after fun getFeed(): LiveData> { val data = MutableLiveData>() async(UI) { val deferred = bg { GetRssFeedRequest.getFeed() } val response = deferred.await() if (response != null) { if(response.isSuccessful) { data.value = Resource.success(response.body()!!) } else { data.value = Resource.error() } } } return data }

Slide 29

Slide 29 text

Anko Coroutines - after & shorter fun getFeed(): LiveData> { val data = MutableLiveData>() async(UI) { val response: Response? = bg { GetRssFeedRequest.getFeed() }.await() data.value = if(response?.isSuccessful == true) Resource.success(response.body()!!) else Resource.error() } return data }

Slide 30

Slide 30 text

Anko Coroutines - Activity override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) async(UI) { val deferred: Deferred?> = bg { GetRssFeedRequest.getFeed() } val response: Response? = deferred.await() if(response != null && response.isSuccessful) { manageRss(response.body()) } } } Any Leak here?

Slide 31

Slide 31 text

Anko Coroutines - Activity TRW override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val ref = asReference() async(UI) { val deferred: Deferred?> = bg { GetRssFeedRequest.getFeed() } val response: Response? = deferred.await() if(response != null && response.isSuccessful) { ref().manageRss(response.body()) } } } No leaks!

Slide 32

Slide 32 text

Questions?

Slide 33

Slide 33 text

Giveaways!

Slide 34

Slide 34 text

3 Jetbrains licenses

Slide 35

Slide 35 text

Kotlin In Action val coupon45 = “KOTLINTLV”

Slide 36

Slide 36 text

Thank you @galex