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

Lithoを使ってみた

 Lithoを使ってみた

2017/05/09 shibuya.apk #14

934a9e49edc3174d09ab2e09daed5062?s=128

ymnder

May 09, 2017
Tweet

Transcript

  1. LithoΛ࢖ͬͯΈͨ 2017/05/09 shibuya.apk #14 

  2. sample App https://github.com/ymnder/BLite 2

  3. whoami • Ryo Yamazaki / Nikkei Application Engineer • twitter:@ymnd

    • github:@ymnder • google i/o ߦ͖·͢ 3
  4. product 4

  5. ࠓ೔ͷ͓඼ॻ͖ ᶃLithoͱ͸ ᶄLithoͰॻ͍ͯΈͨ ᶅ࢖ͬͯΈͯͲ͏͔ͩͬͨ 5

  6. Lithoͱ͸ • facebook੡ͷUI framework ʢv0.2.0ʣminSDK 15 • ReactͱComponentKitʹΠϯεύΠΞ͞Ε͍ͯΔ • RecyclerViewʹج͍ͮͨෳࡶ͔ͭεΫϩʔϥϒϧͳUI

    Λ࣮૷͢ΔͨΊʹͭ͘ΒΕͨ 6
  7. Lithoͷڧ͍ͱ͜Ζ • RecyclerView • 1000ms / 60fps ≒ 16ms •

    όΠϯυɾଌఆɾϨΠΞ΢τ·ͰΛߦ͏ඞཁ • FacebookͷNewsFeedͷΑ͏ͳͨ͘͞ΜͷViewType͕ ૝ఆ͞ΕΔ৔߹ʹޮՌΛൃش͢Δ 7
  8. LithoͷΞʔΩςΫνϟ ᶃDeclarative ᶄAsynchronous layout ᶅFlatter view hierarchies ᶆFine-grained recycling 8

  9. ᶃDeclarative: એݴܕ • UIίϯϙʔωϯτΛఆٛ͢ΔએݴܕAPIΛ࢖༻͢Δ • immutableͳೖྗʹج͖ͮUIͷϨΠΞ΢τΛهड़͢Δ • code genarationͰίʔυΛγϯϓϧ͔ͭ؆୯ʹอͯΔ 9

  10. ᶄAsynchronous layout: ඇಉظϨΠΞ΢τ • UIεϨουΛϒϩοΫͤͣʹࣄલʹUIΛଌఆ͠ϨΠΞ ΢τͰ͖Δ 10

  11. ᶅFlatter view hierarchies: ϑϥοτͳViewHierarchy • ϨΠΞ΢τʹYogaΛ࢖༻͠ɺࣗಈతʹUIؚ͕Ή ViewGroupsΛݮΒ͢ 11

  12. ᶅFlatter view hierarchies: ϑϥοτͳViewHierarchy • දࣔཁૉ͕ҰຕͷϑϥοτͳଘࡏʹͳΔ • ࣮ߦ࣌ʹ͸View͸LithoViewԼͷComponentHostʹू ໿͞ΕΔ 12

  13. ConstraintLayoutͱͷࠩҟ • ྆ํͱ΋Viewͷ֊૚Λϑϥοτʹͯ͘͠ΕΔ • Litho͸FlexboxΛ࣮ݱ͢ΔYogaΛ࢖༻͢Δ • xmlͰͳ࣮͘૷ͯ͠૊Ή • දࣔ࣌ʹTextViewͳͲͷཁૉΛશ෦̍ຕʹ·ͱΊΔ 13

  14. ᶆFine-grained recycling: ͖Ίࡉ͔͍ϦαΠΫϧ • ֤ςΩετɾը૾ɾө૾ͷΑ͏ͳUIΞΠςϜ͸ݸผʹϦ αΠΫϧ͞ΕΔ • RecyclerViewͱҟͳΓɺListItem୯ҐͰ࠶ར༻͞Εͣɺ ListItemͷதͷཁૉ୯ҐͰ࠶࢖༻͞ΕΔ 14

  15. ࣮ࡍʹ࢖ͬͯΈΑ͏ 15

  16. LithoͰॻ͍ͯΈͨ dependencies { //Litho compile 'com.facebook.litho:litho-core:0.2.0' //contains basic component compile

    'com.facebook.litho:litho-widget:0.2.0' //annotation processor compile 'com.facebook.litho:litho-annotations:0.2.0' annotationProcessor 'com.facebook.litho:litho-processor:0.2.0' //for Yoga. Yoga has dependencies for native compile 'com.facebook.soloader:soloader:0.2.0' //Stetho plugin debugCompile 'com.facebook.litho:litho-stetho:0.2.0' } 16
  17. LithoͰॻ͍ͯΈͨ public class SplashActivity extends AppCompatActivity { @Override protected void

    onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //ContextͷαϒΫϥεͰϥΠϒϥϦ಺Ͱ࢖͏৘ใΛ؅ཧ͢Δ final ComponentContext c = new ComponentContext(this); setContentView( //ComponentΛmountͨ͠ViewGroupΛฦ͢ LithoView.create( this, SplashComponent.create(c).build())); } } 17
  18. ComponentΛॻ͍ͯΈͨ //ଞͷίϯϙʔωϯτΛ·ͱΊΔίϯϙʔωϯτ @LayoutSpec public class SplashComponentSpec { @OnCreateLayout static ComponentLayout

    onCreateLayout(ComponentContext c) { return Column.create(c) .child( Text.create(c) .text(“Hello Litho :)”) .textSizeSp(30f) .withLayout() .alignSelf(YogaAlign.CENTER) ).justifyContent(YogaJustify.CENTER) .build(); } } 18
  19. ComponentΛॻ͍ͯΈͨ 19

  20. ίʔυͷઆ໌ 20

  21. LithoͰॻ͍ͯΈͨ public class SplashActivity extends AppCompatActivity { @Override protected void

    onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //ContextͷαϒΫϥεͰϥΠϒϥϦ಺Ͱ࢖͏৘ใΛ؅ཧ͢Δ final ComponentContext c = new ComponentContext(this); setContentView( //ComponentΛmountͨ͠ViewGroupΛฦ͢ LithoView.create( this, SplashComponent.create(c).build())); } } 21
  22. LithoͰॻ͍ͯΈͨ public class SplashActivity extends AppCompatActivity { @Override protected void

    onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //ContextͷαϒΫϥεͰϥΠϒϥϦ಺Ͱ࢖͏৘ใΛ؅ཧ͢Δ final ComponentContext c = new ComponentContext(this); setContentView( //ComponentΛmountͨ͠ViewGroupΛฦ͢ LithoView.create( this, SplashComponent.create(c).build())); } } 22
  23. LithoͰॻ͍ͯΈͨ public class SplashActivity extends AppCompatActivity { @Override protected void

    onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //ContextͷαϒΫϥεͰϥΠϒϥϦ಺Ͱ࢖͏৘ใΛ؅ཧ͢Δ final ComponentContext c = new ComponentContext(this); setContentView( //ComponentΛmountͨ͠ViewGroupΛฦ͢ LithoView.create( this, SplashComponent.create(c).build())); } } 23
  24. ComponentΛॻ͍ͯΈͨ //ଞͷίϯϙʔωϯτΛ·ͱΊΔίϯϙʔωϯτ @LayoutSpec public class SplashComponentSpec { @OnCreateLayout static ComponentLayout

    onCreateLayout(ComponentContext c) { return Column.create(c) .child( Text.create(c) .text(“Hello Litho :)”) .textSizeSp(30f) .withLayout() .alignSelf(YogaAlign.CENTER) ).justifyContent(YogaJustify.CENTER) .build(); } } 24
  25. ComponentΛॻ͍ͯΈͨ //ଞͷίϯϙʔωϯτΛ·ͱΊΔίϯϙʔωϯτ @LayoutSpec public class SplashComponentSpec { @OnCreateLayout static ComponentLayout

    onCreateLayout(ComponentContext c) { return Column.create(c) .child( Text.create(c) .text(“Hello Litho :)”) .textSizeSp(30f) .withLayout() .alignSelf(YogaAlign.CENTER) ).justifyContent(YogaJustify.CENTER) .build(); } } 25
  26. ComponentͷΞϊςʔγϣϯ • LayoutSpecʢྫɿListItemʣ • ଞͷίϯϙʔωϯτΛಛఆͷϨΠΞ΢τʹ·ͱΊΔɻ ViewGroupతͳଘࡏ • MountSpecʢྫɿRecyclerʣ • View΍DrawableΛϨϯμϦϯά͢ΔɻϨΠΞ΢τͷ

    ܭࢉॲཧ͕ఆٛ͞ΕΔ 26
  27. ComponentΛॻ͍ͯΈͨ //ଞͷίϯϙʔωϯτΛ·ͱΊΔίϯϙʔωϯτ @LayoutSpec public class SplashComponentSpec { @OnCreateLayout static ComponentLayout

    onCreateLayout(ComponentContext c) { return Column.create(c) .child( Text.create(c) .text(“Hello Litho :)”) .textSizeSp(30f) .withLayout() .alignSelf(YogaAlign.CENTER) ).justifyContent(YogaJustify.CENTER) .build(); } } 27
  28. ComponentΛॻ͍ͯΈͨ //ଞͷίϯϙʔωϯτΛ·ͱΊΔίϯϙʔωϯτ @LayoutSpec public class SplashComponentSpec { @OnCreateLayout static ComponentLayout

    onCreateLayout(ComponentContext c) { return Column.create(c) .child( Text.create(c) .text(“Hello Litho :)”) .textSizeSp(30f) .withLayout() .alignSelf(YogaAlign.CENTER) ).justifyContent(YogaJustify.CENTER) .build(); } } 28
  29. Layout • ֤ཁૉΛͲͷํ޲ʹ഑ஔ͢Δ͔ܾΊΔίϯςφ • ೖΕࢠʹͯ͠ෳࡶͳ഑ஔΛߦ͏͜ͱ΋ՄೳͰ͋Δ //linear layout: horizontal Row.create(c) .child(…)

    .build(); //linear layout: vertical Column.create(c) .child(…) .build(); 29
  30. ComponentΛॻ͍ͯΈͨ //ଞͷίϯϙʔωϯτΛ·ͱΊΔίϯϙʔωϯτ @LayoutSpec public class SplashComponentSpec { @OnCreateLayout static ComponentLayout

    onCreateLayout(ComponentContext c) { return Column.create(c) .child( Text.create(c) .text(“Hello Litho :)”) .textSizeSp(30f) .withLayout() .alignSelf(YogaAlign.CENTER) ).justifyContent(YogaJustify.CENTER) .build(); } } 30
  31. ComponentΛॻ͍ͯΈͨ //ଞͷίϯϙʔωϯτΛ·ͱΊΔίϯϙʔωϯτ @LayoutSpec public class SplashComponentSpec { @OnCreateLayout static ComponentLayout

    onCreateLayout(ComponentContext c) { return Column.create(c) .child( Text.create(c) .text(“Hello Litho :)”) .textSizeSp(30f) .withLayout() .alignSelf(YogaAlign.CENTER) ).justifyContent(YogaJustify.CENTER) .build(); } } 31
  32. Component • ࠷ॳ͔Β༻ҙ͞Ε͍ͯΔجຊతͳView //like TextView Text.create(c); //like ImageView Image.create(c); 32

  33. Recycler @OnCreateLayout static ComponentLayout onCreateLayout(final ComponentContext c) { final RecyclerBinder

    recyclerBinder = new RecyclerBinder(c, new LinearLayoutInfo(c, VERTICAL, false)); final RecyclerEventsController controller = new RecyclerEventsController(); //... return Recycler.create(c) .binder(recyclerBinder) .recyclerEventsController(controller) .refreshHandler(ListComponent.onPTRrefresh(c, recyclerBinder, controller)) .onScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); //... } }) .build(); } 33
  34. Recycler @OnCreateLayout static ComponentLayout onCreateLayout(final ComponentContext c) { final RecyclerBinder

    recyclerBinder = new RecyclerBinder(c, new LinearLayoutInfo(c, VERTICAL, false)); final RecyclerEventsController controller = new RecyclerEventsController(); //... return Recycler.create(c) .binder(recyclerBinder) .recyclerEventsController(controller) .refreshHandler(ListComponent.onPTRrefresh(c, recyclerBinder, controller)) .onScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); //... } }) .build(); } 34
  35. Recycler • dataͱviewΛbind͢ΔଘࡏɻAdpaterͱLayoutManagerͷػೳΛ࣋ͭ • ୈೋҾ਺ʹLayoutManagerΛwrap͢ΔLayoutInfoΛͱΔɻ • LinearLayoutInfoͷ৔߹͸ɺLinearLayoutManagerΛ಺෦తʹ͍࣋ͬͯΔ • GridLayoutInfo΋༻ҙ͞Ε͍ͯΔ final

    RecyclerBinder recyclerBinder = new RecyclerBinder(c, new LinearLayoutInfo(c, VERTICAL, false)); 35
  36. Recycler @OnCreateLayout static ComponentLayout onCreateLayout(final ComponentContext c) { final RecyclerBinder

    recyclerBinder = new RecyclerBinder(c, new LinearLayoutInfo(c, VERTICAL, false)); final RecyclerEventsController controller = new RecyclerEventsController(); //... return Recycler.create(c) .binder(recyclerBinder) .recyclerEventsController(controller) .refreshHandler(ListComponent.onPTRrefresh(c, recyclerBinder, controller)) .onScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); //... } }) .build(); } 36
  37. Recycler @OnCreateLayout static ComponentLayout onCreateLayout(final ComponentContext c) { final RecyclerBinder

    recyclerBinder = new RecyclerBinder(c, new LinearLayoutInfo(c, VERTICAL, false)); final RecyclerEventsController controller = new RecyclerEventsController(); //... return Recycler.create(c) .binder(recyclerBinder) .recyclerEventsController(controller) .refreshHandler(ListComponent.onPTRrefresh(c, recyclerBinder, controller)) .onScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); //... } }) .build(); } 37
  38. Recycler • Recycler͸RecyclerViewΛ಺෦తʹ࣋ͭRecyclerViewWrapperΛ༗͢Δ • RecyclerViewWrapper͸SwipeRefreshLayoutΛܧঝ͍ͯ͠Δ • ͜ΕΛίϯτϩʔϧ͢Δ͢ΔͨΊʹcontrollerΛ࡞੒͍ͯ͠Δ final RecyclerEventsController controller

    = new RecyclerEventsController(); 38
  39. Recycler @OnCreateLayout static ComponentLayout onCreateLayout(final ComponentContext c) { final RecyclerBinder

    recyclerBinder = new RecyclerBinder(c, new LinearLayoutInfo(c, VERTICAL, false)); final RecyclerEventsController controller = new RecyclerEventsController(); //... return Recycler.create(c) .binder(recyclerBinder) .recyclerEventsController(controller) .refreshHandler(ListComponent.onPTRrefresh(c, recyclerBinder, controller)) .onScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); //... } }) .build(); } 39
  40. Recycler @OnCreateLayout static ComponentLayout onCreateLayout(final ComponentContext c) { final RecyclerBinder

    recyclerBinder = new RecyclerBinder(c, new LinearLayoutInfo(c, VERTICAL, false)); final RecyclerEventsController controller = new RecyclerEventsController(); //... return Recycler.create(c) .binder(recyclerBinder) .recyclerEventsController(controller) .refreshHandler(ListComponent.onPTRrefresh(c, recyclerBinder, controller)) .onScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); //... } }) .build(); } 40
  41. Event • EventΛड͚औΓ͍ͨComponentʹఆٛ͢Δ @OnEvent(PTRRefreshEvent.class) static void onPTRrefresh(final ComponentContext c, @Param

    final RecyclerBinder recyclerBinder, @Param final RecyclerEventsController controller){ //… controller.clearRefreshing(); } 41
  42. Stetho plugin • Stetho(v1.5.0)ͱ࿈ܞ • re-start / re-compileͤͣʹमਖ਼Ͱ͖Δ • ࣮ࡍʹඍमਖ਼ΛՃ͑ͳ͕Βಈ͖Λ͔֬ΊΒΕΔ

    • Yogaͱ΋༑ୡʹͳΕͦ͏ • AndroidStudioͷlayout inspectorͰ͸ϑϥοτͳView ʹͳ͍ͬͯΔ 42
  43. StethoͰ͍ͬͯ͡ΈΑ͏ 43

  44. debug options • ComponentsConfigurationͷ஋Λtrueʹ͢Δͱλον ΤϦΞͱViewͷڥքΛͦΕͧΕՄࢹԽͰ͖Δ 44

  45. debug options ᶃdebugHighlightInteractiveBounds λονΤϦΞΛՄࢹԽ 45

  46. debug options ᶄdebugHighlightMountBounds ViewͷڥքΛՄࢹԽ 46

  47. ࢖ͬͯΈͯͲ͏͔ͩͬͨ • xmlͰͳ͍ܗͰViewΛ૊Ήͷ͕৽઱ͩͬͨ • YogaʹΑΓࣗಈతʹྑ͍Ґஔʹௐ੔͞ΕΔͷָ͕ 47

  48. ࢖ͬͯΈͯͲ͏͔ͩͬͨ • ίʔυΛ௥͍ʹ͍͘ • ࣗಈੜ੒ޙͷΫϥεΛࢀর͢ΔͨΊɺfind usage͕΍Γʹ͍͘ • annotation processorނʹίϯύΠϧϑΣʔζʹ࣮ߦ͞ΕΔ •

    SpecʹΞϊςʔγϣϯΛ௥Ճͨ͠৔߹࠶Ϗϧυ͕ඞཁͰ͋Δ • δΣενϟʔ΍ΞχϝʔγϣϯΛ൐͏μΠφϛοΫͳUI͸ࠓͷͱ ͜Ζͳ͍ • ࠓޙ௥Ճ͞ΕΔΒ͍͠ • xmlͰͷैདྷͷϨΠΞ΢τΛ͢΂ͯ୅ସ͢Δ΋ͷͰ͸ͳ͍ • AndroidStudioͰPreview͢Δ͜ͱ͕Ͱ͖ͳ͍ 48
  49. ࢖ͬͯΈͯͲ͏͔ͩͬͨ • ෳࡶͳViewHolderͷ૊Έ߹Θ͕ͤͳ͍ݶΓ͸ෆཁʁ 49

  50. ΋ͬͱௐ͍ࠪͨ͠ͱ͜Ζ • Asynchronous Layout • Immutability and thread safety •

    http://fblitho.com/docs/asynchronous-layout • UIεϨουʹ౉͢લʹlayoutͱmeasureΛߦ͍ͬͯΔ • ͲͷΑ͏ʹόοΫάϥ΢ϯυͰॲཧ͞ΕͯΔͷ͔ʁ 50
  51. ͓ΘΓ 51

  52. Notes 

  53. ༨ஊɿ຤ඌͷSpecͱ͸ • ྫɿNameComponentSpec • ίϯϙʔωϯτͷ໊લ͸SpecͰऴΘΒͤΔ • annotation processorͰNameComponentʹͳΔ • javapoet࢖༻͍ͯ͠Δɻ຤ඌʹSpecΛ͚ͭΔɻ

    • ɾhttps://github.com/facebook/litho/blob/master/ litho-processor/src/main/java/com/facebook/litho/ specmodels/model/SpecModelValidation.java#L53 53
  54. ੡࡞ऀʹΑΔ঺հϒϩά • Lithoʹ͍ͭͯ෼͔Γ΍͘͢঺հ͞Ε͍ͯΔ • https://code.facebook.com/posts/ 1187475984695956/open-sourcing-litho-a- declarative-ui-framework-for-android/ • LithoͷίϯηϓτɿComponents for

    Android • https://code.facebook.com/posts/ 531104390396423/components-for-android-a- declarative-framework-for-efficient-uis 54
  55. ϒϩάͳͲ • CustomButtonΛͭͬͯ͘Έͨ • https://medium.com/@p.tournaris/litho-how-to-create-a-custom- button-b460b5a3b828 • LithoͷReview • https://medium.com/proandroiddev/facebook-litho-review-

    b591372d85be • DroidCon London 2016 • https://skillsmatter.com/skillscasts/8418-flat-as-a-pancake#video 55
  56. ͦͷଞ • https://news.ycombinator.com/item?id=14142321 • https://www.reddit.com/r/androiddev/comments/ 68i0hg/ review_of_the_new_facebook_litho_framework_part_1/ • https://www.reddit.com/r/androiddev/comments/ 66g0ve/facebook_for_developers_litho_a_declarative/

    • https://www.reddit.com/r/androiddev/comments/ 6659bn/litho_a_declarative_ui_framework_for_android/ • https://www.reddit.com/r/androiddev/comments/ 6784bv/glide_imageloading_component_for_litho/ 56