Slide 1

Slide 1 text

Ko#n Android Extensionsͷத਎ͱ͜Ε͔Β @RyotaMurohoshi 2017/11/09 ୈ7ճ Kotlinษڧձ @SanSan

Slide 2

Slide 2 text

Kotlin Android Extensions ࢖͍ͬͯΔਓʁ

Slide 3

Slide 3 text

΍ͬͺΓDataBindingͰ͔͢ʁ ͦΕͱ΋ankoͰ͔͢ʁ

Slide 4

Slide 4 text

Kotlin Android Extensions ๻͸ɺ޷͖Ͱ͢

Slide 5

Slide 5 text

ɹࣗݾ঺հ @RyotaMurohoshi

Slide 6

Slide 6 text

࠷ۙձࣾͰɺKotlinຊಡॻձΛ࢝Ί·ͨ͠

Slide 7

Slide 7 text

Tools > Kotlin > Show Kotlin Bytecode > Decompile ͜ΕΛԿΑΓઌʹڭ͑ͯ͋͛Δͱ͍͍ͱࢥ͏ʂ

Slide 8

Slide 8 text

ʮKo#n Android Extensionsͷ த਎ͱ͜Ε͔Βʯ ʹ͍ͭͯ࿩͠·͢ Kotlin 1.1.51ʹͯ

Slide 9

Slide 9 text

Kotlin Android Extensions ʹ͍͓ͭͯ͞Β͍

Slide 10

Slide 10 text

ViewͷΠϯελϯεʹ௒؆୯ʹ ΞΫηεͰ͖Δ΍ͭ

Slide 11

Slide 11 text

gadleͷதͰ͜͏ॻ͘ (͍ͯ͏͔σϑΥϧτͰೖ͍ͬͯΔ) apply plugin: 'kotlin-android-extensions'

Slide 12

Slide 12 text

Slide 13

Slide 13 text

ίʔυͰtextViewͱ͍͏໊લͰ TextViewܕͱͯ͠ΞΫηεͰ͖Δ class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // TextViewܕͷtextView͕ʂ textView.text = "Hello Activity" } }

Slide 14

Slide 14 text

// TextViewܕͷtextView͕ʂ textView.text = "Hello Activity"

Slide 15

Slide 15 text

؆୯Ͱ͠ΐʂ ศརͰ͠ΐʂ

Slide 16

Slide 16 text

த਎ΈͯΈ·͠ΐ͏

Slide 17

Slide 17 text

Tools > Kotlin > Show Kotlin Bytecode > Decompile

Slide 18

Slide 18 text

class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // TextViewܕͷtextView͕ʂ textView.text = "Hello Activity" } }

Slide 19

Slide 19 text

/* ུ */ public final class MainActivity extends AppCompatActivity { private HashMap _$_findViewCache; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131296283); ((TextView)this._$_findCachedViewById(id.textView)).setText((CharSequence)"Hello Activity"); } 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) { var2 = this.findViewById(var1); this._$_findViewCache.put(Integer.valueOf(var1), var2); } return var2; } public void _$_clearFindViewByIdCache() { if(this._$_findViewCache != null) { this._$_findViewCache.clear(); } } }

Slide 20

Slide 20 text

/* ུ */ public final class MainActivity extends AppCompatActivity { private HashMap _$_findViewCache; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131296283); ((TextView)this._$_findCachedViewById(id.textView)) .setText((CharSequence)"Hello Activity"); } 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) { var2 = this.findViewById(var1); this._$_findViewCache.put(Integer.valueOf(var1), var2); } return var2; } /* ུ */

Slide 21

Slide 21 text

σίϯύΠϧͯ͠ΈΔͱ • _$_findViewCacheͱ͍͏HashMapΛϑΟʔϧυͱͯ࣋ͭ͠ • _$_findCachedViewByIdϝιου͸ɺ • Ҿ਺ʹͱͬͨid͕HashMapʹ͋Ε͹ͦΕΛฦ͠ • ͳ͚Ε͹୳ࡧɺHashMapʹೖΕͨޙɺͦΕΛฦ͢ _$_findViewCache΍_findCachedViewById͸ɺKotlinͰ͸ίʔυͰ͔͚ͳ͍ɻཁʮ``ʯ

Slide 22

Slide 22 text

/* ུ */ public final class MainActivity extends AppCompatActivity { private HashMap _$_findViewCache; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131296283); ((TextView)this._$_findCachedViewById(id.textView)) .setText((CharSequence)"Hello Activity"); } 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) { var2 = this.findViewById(var1); this._$_findViewCache.put(Integer.valueOf(var1), var2); } return var2; } /* ུ */

Slide 23

Slide 23 text

͜Μͳײ͡ͷ֦ுϓϩύςΟ͕ੜ͑ΔΘ͚Ͱ͸ͳ͍ʂ // ͜Μͳײ͡͡Όͳ͍ʂ // Ωϟογϡػߏ͋ΔΑ val MainActivity.textView : TextView get() = findViewById(R.id.textView)

Slide 24

Slide 24 text

FragmentͰ΋Kotlin Android Extensions࢖͑·͢ FragmentͰ΋ಉ͡Α͏ʹHashMapͰͷΩϟογϡػߏʂ

Slide 25

Slide 25 text

֦ுؔ਺ͷதͰ΋ʂ package com.teammurosta import kotlinx.android.synthetic.main.activity_main.* fun MainActivity.updateText(text: String) { this.textView.text = text }

Slide 26

Slide 26 text

import android.os.Bundle import android.support.v7.app.AppCompatActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) updateText("Hello") } }

Slide 27

Slide 27 text

// ֦ுؔ਺ͷσίϯύΠϧ݁Ռ public final class MainActivityExtensionsKt { public static final void updateText(@NotNull MainActivity $receiver, @NotNull String text) { Intrinsics.checkParameterIsNotNull($receiver, "$receiver"); Intrinsics.checkParameterIsNotNull(text, "text"); ((TextView)$receiver._$_findCachedViewById(id.textView)).setText((CharSequence)text); } }

Slide 28

Slide 28 text

// MainActivityͷσίϯύΠϧ݁Ռ public final class MainActivity extends AppCompatActivity { private HashMap _$_findViewCache; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131296283); MainActivityExtensionsKt.updateText(this, "Hello"); } 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) { var2 = this.findViewById(var1); this._$_findViewCache.put(Integer.valueOf(var1), var2); } return var2; } public void _$_clearFindViewByIdCache() { if(this._$_findViewCache != null) { this._$_findViewCache.clear(); } } }

Slide 29

Slide 29 text

Կ͕ԿͰ΋Ωϟογϡ͢ΔΘ͚͡Όͳ͍

Slide 30

Slide 30 text

֦ுؔ਺ͷதͰ΋ʂMainAc'vity -> Ac'vityʹมߋʂ package com.teammurosta import kotlinx.android.synthetic.main.activity_main.* fun Activity.updateText(text: String) { this.textView.text = text }

Slide 31

Slide 31 text

// ֦ுؔ਺ͷσίϯύΠϧ݁Ռ public final class MainActivityExtensionsKt { public static final void updateText(@NotNull Activity $receiver, @NotNull String text) { Intrinsics.checkParameterIsNotNull($receiver, "$receiver"); Intrinsics.checkParameterIsNotNull(text, "text"); ((TextView)$receiver.findViewById(id.textView)).setText((CharSequence)text); } }

Slide 32

Slide 32 text

android.app.Ac+vityΫϥε΍ android.support.v7.app.AppCompatAc2vityΫϥεɺ android.support.v4.app.FragmentΫϥεΛ֦ுͨ͠৔߹ɺ ී௨ʹfindViewByIdΛݺͿ

Slide 33

Slide 33 text

ViewHolderͰͷ׆༻ྫ

Slide 34

Slide 34 text

Kotlin Android Extensions Λ࢖ͬͯBind import kotlinx.android.synthetic.main.listitem_product.view.* class ProductViewHolder(containerView: View) : RecyclerView.ViewHolder(containerView) { fun bind(product: Product) { itemView.nameTextView.text = product.name itemView.idTextView.text = product.id } }

Slide 35

Slide 35 text

σίϯύΠϧ݁ՌΛΈΔͱΩϟογϡ͍ͯ͠ͳ͍ public final class ProductViewHolder extends ViewHolder { public final void bind(@NotNull Product product) { Intrinsics.checkParameterIsNotNull(product, "product"); ((TextView)this.itemView.findViewById(id.nameTextView)).setText((CharSequence)product.getName()); ((TextView)this.itemView.findViewById(id.idTextView)).setText((CharSequence)product.getId()); } public ProductViewHolder(@NotNull View containerView) { Intrinsics.checkParameterIsNotNull(containerView, "containerView"); super(containerView); } }

Slide 36

Slide 36 text

͔͜͜Βઌ͸ɺ͜Ε͔Βͷ࿩

Slide 37

Slide 37 text

Kotlin 1.1.4ͰύϫʔΞοϓ experimental͚ͩͲ androidExtensions { experimental = true }

Slide 38

Slide 38 text

1. Ωϟογϡઓུ

Slide 39

Slide 39 text

@ContainerOp,ons

Slide 40

Slide 40 text

ࠓ·ͰΩϟογϡ͸HashMapͷΈ ʮSparseArrayʯͱʮΩϟογϡ͠ͳ͍ʯ Λબ΂ΔΑ͏ʹͳͬͨʂ

Slide 41

Slide 41 text

// ↓Ξϊςʔγϣϯ஫໨ @ContainerOptions(CacheImplementation.SPARSE_ARRAY) class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) textView.text = "Hello" } }

Slide 42

Slide 42 text

public final class MainActivity extends Activity { private SparseArray _$_findViewCache; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((TextView)this._$_findCachedViewById(id.textView)).setText((CharSequence)"Hello"); } public View _$_findCachedViewById(int var1) { if(this._$_findViewCache == null) { this._$_findViewCache = new SparseArray(); } View var2 = (View)this._$_findViewCache.get(var1); if(var2 == null) { var2 = this.findViewById(var1); this._$_findViewCache.put(var1, var2); } return var2; } public void _$_clearFindViewByIdCache() { if(this._$_findViewCache != null) { this._$_findViewCache.clear(); } } }

Slide 43

Slide 43 text

// ↓Ξϊςʔγϣϯ஫໨ @ContainerOptions(CacheImplementation.NO_CACHE) class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) textView.text = "Hello" } }

Slide 44

Slide 44 text

public final class MainActivity extends Activity { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((TextView)this.findViewById(id.textView)).setText((CharSequence)"Hello"); } }

Slide 45

Slide 45 text

Ұׅมߋ΋Ͱ͖Δ ະυΩϡϝϯτԽͰ࢓༷มߋ͢Δ͔΋͠Εͳ͍͚Ͳ

Slide 46

Slide 46 text

app/build.gradle಺Ͱઃఆ androidExtensions { experimental = true defaultCacheImplementation = "sparse_array" // hash_map or none }

Slide 47

Slide 47 text

ΩϟογϡʹSparseArrayɺ࢖ͬͨ΄͏͕͍͍ͷʁ

Slide 48

Slide 48 text

Do we need to change the default cache implementa3on to SparseArray? Looks like no, because "It is generally slower than a tradi8onal HashMap, since lookups require a binary search and adds and removes require inser8ng and dele8ng entries in the array" Kotlin KEEPΑΓ

Slide 49

Slide 49 text

2. ViewHolderରԠ

Slide 50

Slide 50 text

Kotlin Android Extensions Λ࢖ͬͯBind import kotlinx.android.synthetic.main.listitem_product.view.* class ProductViewHolder(containerView: View) : RecyclerView.ViewHolder(containerView) { fun bind(product: Product) { itemView.nameTextView.text = product.name itemView.idTextView.text = product.id } }

Slide 51

Slide 51 text

σίϯύΠϧ݁ՌΛΈΔͱΩϟογϡ͍ͯ͠ͳ͍ public final class ProductViewHolder extends ViewHolder { public final void bind(@NotNull Product product) { Intrinsics.checkParameterIsNotNull(product, "product"); ((TextView)this.itemView.findViewById(id.nameTextView)).setText((CharSequence)product.getName()); ((TextView)this.itemView.findViewById(id.idTextView)).setText((CharSequence)product.getId()); } public ProductViewHolder(@NotNull View containerView) { Intrinsics.checkParameterIsNotNull(containerView, "containerView"); super(containerView); } }

Slide 52

Slide 52 text

Ac#vity΍FragmentͷαϒΫϥεΈ͍ͨʹ Ωϟογϡ͢Δʹ͸ʁ

Slide 53

Slide 53 text

LayoutContainer

Slide 54

Slide 54 text

LayoutContainerΛ࣮૷͢Δ import kotlinx.android.synthetic.main.listitem_product.* class ProductViewHolder(override val containerView: View /*<= ஫໨ */) : RecyclerView.ViewHolder(containerView), LayoutContainer /*<= ஫໨ */ { fun bind(product: Product) { // ஫໨ nameTextView.text = product.name idTextView.text = product.id } }

Slide 55

Slide 55 text

Ωϟογϡ͢ΔΑ͏ʹʂ public final class ProductViewHolder extends ViewHolder implements LayoutContainer { @NotNull private final View containerView; private HashMap _$_findViewCache; public final void bind(@NotNull Product product) { Intrinsics.checkParameterIsNotNull(product, "product"); ((TextView)this._$_findCachedViewById(id.nameTextView)).setText((CharSequence)product.getName()); ((TextView)this._$_findCachedViewById(id.idTextView)).setText((CharSequence)product.getId()); } @NotNull public View getContainerView() { return this.containerView; } public ProductViewHolder(@NotNull View containerView) { Intrinsics.checkParameterIsNotNull(containerView, "containerView"); super(containerView); this.containerView = containerView; } public View _$_findCachedViewById(int var1) { if(this._$_findViewCache == null) { this._$_findViewCache = new SparseArray(); } View var2 = (View)this._$_findViewCache.get(var1); if(var2 == null) { View var10000 = this.getContainerView(); if(var10000 == null) { return null; } var2 = var10000.findViewById(var1); this._$_findViewCache.put(var1, var2); } return var2; } public void _$_clearFindViewByIdCache() { if(this._$_findViewCache != null) { this._$_findViewCache.clear(); } } }

Slide 56

Slide 56 text

3. ParcelableରԠ

Slide 57

Slide 57 text

@Parcelize

Slide 58

Slide 58 text

@SuppressLint("ParcelCreator") @Parcelize data class Product(val name: String, val id: String) : Parcelable

Slide 59

Slide 59 text

public final int describeContents() { return 0; } public final void writeToParcel(@NotNull Parcel parcel, int flags) { Intrinsics.checkParameterIsNotNull(parcel, "parcel"); parcel.writeString(this.name); parcel.writeString(this.id); } public static class Creator implements android.os.Parcelable.Creator { @NotNull public final Product[] newArray(int size) { return new Product[size]; } @NotNull public final Object createFromParcel(@NotNull Parcel in) { Intrinsics.checkParameterIsNotNull(in, "in"); return new Product(in.readString(), in.readString()); } }

Slide 60

Slide 60 text

ͨͩɺAndroid 4ܥͷҰ෦ͷ୺຤Ͱ Πϯετʔϧʹࣦഊ͢ΔΈ͍ͨ ͜Ε͔Βʹظ଴ʂ 4.1ͷEmulatorͰࣦഊΛ֬ೝࡁΈ

Slide 61

Slide 61 text

KEEPʹ͸ɺʮSealdΫϥεʹ΋ରԠΛݕ౼ʯͬͯॻ͍ͯ͋Δ ࣮ࡍ͸·ͩͰ͖ͳ͍Έ͍ͨʁͲ͏ͳΔͩΖ͏ʁ

Slide 62

Slide 62 text

ࠓޙʹظ଴ʂ

Slide 63

Slide 63 text

ͶɺKotlin Android Extensions ͍͍Ͱ͠ΐʂ

Slide 64

Slide 64 text

·ͱΊ • DataBinding΋͍͍͚ͲɺKotlin Android Extensions΋Ͷ • Parcelize͸ɺࠓޙʹظ଴ʂ • KEEPΈΔͷָ͍͠Αʂ

Slide 65

Slide 65 text

όάʁ Kotlin Android Extensions͕༗ޮʹͳͬͯͳ͍ ϓϩδΣΫͰ΋Ac#vity΍Fragmentʹ _$_findCachedViewByIdͱ͔͕Ͱ͖͍ͯΔʁ

Slide 66

Slide 66 text

Ko#n Android Extensionsͷத਎ͱ͜Ε͔Β @RyotaMurohoshi 2017/11/09 ୈ7ճ Kotlinษڧձ @SanSan