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

GUIアプリケーションの構造と設計

 GUIアプリケーションの構造と設計

Presented at Hatena Engineer Seminar #9

https://hatena.connpass.com/event/69844/

58b40ae3b0db6cf0202a3802e9dd70fe?s=128

cockscomb

October 31, 2017
Tweet

Transcript

  1. (6*ΞϓϦέʔγϣϯͷߏ଄ͱઃܭ

  2. (6*ΞϓϦέʔγϣϯͷߏ଄ͱઃܭ w εϚʔτϑΥϯʢJ04"OESPJEʣ w σεΫτοϓ w 8FCʢ3FBDUʣ

  3. ΞϓϦέʔγϣϯͷઃܭ J04 $PDPBUPVDI "QQ -JCSBSJFT "OESPJE BOESPJE "QQ -JCSBSJFT 8FC

    %0. "QQ -JCSBSJFT ʜ ʜ "QQ -JCSBSJFT ͜͜
  4. εςʔτϑϧ w (6*ΞϓϦέʔγϣϯͷຊ࣭తͳ೉͠͞ w ঢ়ଶΛͲ͏΍ͬͯѻ͏ͷ͔

  5. w 0CTFSWFSύλʔϯ w ΦϒδΣΫτͷੜ੒ɾ؅ཧ w ඇಉظॲཧͷந৅Խ

  6. "OESPJEBQQ ,PUMJO 3Y+BWB %BHHFS %BUB#JOEJOH "SDIJUFDUVSF$PNQPOFOUT

  7. 0CTFSWFSύλʔϯ

  8. 0CTFSWFSύλʔϯ w (P'ͷͷσβΠϯύλʔϯʹ΋਺͑ΒΕΔ w ঢ়ଶΛ؂ࢹͰ͖Δ w Ϟσϧͷঢ়ଶΛ6*ʹ൓өͤ͞Δ
 ˠ6*͸Ϟσϧͷঢ়ଶͷࣸ૾

  9. ୯७ͳ0CTFSWFSύλʔϯ w +BWB#FBOT w ,FZ7BMVF0CTFSWBUJPO J04  w 3FEVYʹ͓͚Δ4UPSFͷTVCTDSJCF 8FC

  10. όΠϯσΟϯά w %BUB#JOEJOH "OESPJE  w $PDPB#JOEJOHT J04  w

    3FBDU 8FC
  11. %BUB#JOEJOH <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <import type="jp.ne.hatena.myapplication.entry.Entry"/> <variable name="entry" type="Entry"/> </data>

    <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/titleView" android:text="@{entry.title}" .../> <TextView android:id="@+id/bodyView" android:text="@{entry.body}" .../> ... </android.support.constraint.ConstraintLayout> </layout>
  12. %BUB#JOEJOH class EntryViewHolder(private val binding: EntryItemBinding): RecyclerView.ViewHolder(binding.root) { companion object

    { fun create(parent: ViewGroup) = EntryViewHolder( EntryItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) ) } fun bindTo(entry: Entry?) { binding.entry = entry } }
  13. σʔλϕʔε w 3PPN "OESPJE  w 3FBMN w $PSF%BUB J04

  14. 3PPN @Entity(tableName = "entry", indices = arrayOf( Index(value = "date")

    )) data class Entry( @PrimaryKey(autoGenerate = true) val id: Int = 0, val title: String, val body: String, val date: Date )
  15. 3PPN @Dao interface EntryDao { @Insert fun insertEntry(entry: Entry) @Update

    fun updateEntry(entry: Entry) @Delete fun deleteEntry(entry: Entry) @Query("SELECT * FROM entry ORDER BY date DESC") fun entriesByDate(): Flowable<List<Entry>> // RxJava 2 }
  16. 3PPN @Dao interface EntryDao { @Insert fun insertEntry(entry: Entry) @Update

    fun updateEntry(entry: Entry) @Delete fun deleteEntry(entry: Entry) @Query("SELECT * FROM entry ORDER BY date DESC") fun entriesByDate(): LiveData<List<Entry>> // LiveData }
  17. 3PPN @Dao interface EntryDao { @Insert fun insertEntry(entry: Entry) @Update

    fun updateEntry(entry: Entry) @Delete fun deleteEntry(entry: Entry) @Query("SELECT * FROM entry ORDER BY date DESC") fun entriesByDate(): LivePagedListProvider<Int, Entry> // Paging Library }
  18. 3PPN class EntriesViewModel @Inject constructor(entryDao: EntryDao): ViewModel() { val entries:

    LiveData<PagedList<Entry>> = entryDao.entriesByDate().create(0, 50) } class EntriesAdapter: PagedListAdapter<Entry, EntryViewHolder>(EntryDiffCallback()) { override fun onBindViewHolder(holder: EntryViewHolder?, position: Int) { val entry = getItem(position) holder?.bindTo(entry) } ... } val entriesAdapter = EntriesAdapter() viewModel.entries.observe(this, Observer { list -> entriesAdapter.setList(list) }) recyclerView.adapter = entriesAdapter
  19. 3FBDUJWF1SPHSBNNJOH w 3Y+BWB "OESPJE  w -JWF%BUB "OESPJE  w

    3Y4XJGU J04  w 3FBDUJWF$PDPB J04  w 3Y+4 8FC
  20. 3Y+BWB fun createEntry(title: String, body: String) = Completable.create { emitter

    -> val entry = Entry(title = title, body = body, date = Date()) // Slow entryDao.insertEntry(entry) emitter.onComplete() }
  21. ΦϒδΣΫτͷੜ੒ɾ؅ཧ

  22. ΦϒδΣΫτͷੜ੒ɾ؅ཧ w ϝϞϦ্ͷΦϒδΣΫτͱ͍͏ঢ়ଶʹ͍ͭͯ w ΦϒδΣΫτΛͲ͏؅ཧ͢Δ͔ w ΦϒδΣΫτ΁ͷࢀরΛͲ͏औಘ͢Δ͔ w ը໘ؒͰͷ৘ใͷ΍ΓऔΓ

  23. 4JOHMFUPOύλʔϯ w 4JOHMFUPO w άϩʔόϧม਺ w 3FEVYͷ4UPSF 8FC

  24. %FQFOEFODZ*OKFDUJPO w %BHHFS "OESPJE  w 3FBDU3FEVYͷDPOOFDU 8FC

  25. %BHHFS w %*$POUBJOFSϑϨʔϜϫʔΫ w ΦϒδΣΫτΛఏڙ͢Δ.PEVMFͱ
 ͦΕΛอ࣋͢Δ$PNQPOFOU w "OESPJE޲͚ͷػೳ͕ॆ࣮

  26. %BHHFS class App: Application(), HasActivityInjector { @Inject lateinit var activityInjector:

    DispatchingAndroidInjector<Activity> private fun applicationInjector() = DaggerAppComponent.builder() .app(this) .build() override fun onCreate() { super.onCreate() applicationInjector().inject(this) } override fun activityInjector() = activityInjector }
  27. %BHHFS @Singleton @Component(modules = arrayOf( AndroidSupportInjectionModule::class, AppModule::class, DatabaseModule::class, ActivityBuilder::class ))

    interface AppComponent { @Component.Builder interface Builder { @BindsInstance fun app(app: App): Builder fun build(): AppComponent } fun inject(app: App) }
  28. %BHHFS @Module abstract class AppModule { @Binds abstract fun bindsContext(app:

    App): Context } @Module abstract class ActivityBuilder { @ContributesAndroidInjector abstract fun contributesMainActivity(): MainActivity } @Module object DatabaseModule { @JvmStatic @Singleton @Provides fun providesAppDatabase(context: Context): Database = Room.databaseBuilder(context, Database::class.java, "diary_db").build() @JvmStatic @Provides fun providesEntryDao(database: Database) = database.entryDao() }
  29. %BHHFS class MainActivity : AppCompatActivity() { @Inject lateinit var entryDao:

    EntryDao ... override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) super.onCreate(savedInstanceState) ... } ... }
  30. ͦͷଞ w "SDIJUFDUVSF$PNQPOFOUTͷ7JFX.PEFM "OESPJE  w $PSF%BUB J04

  31. ඇಉظॲཧͷந৅Խ

  32. ඇಉظॲཧͷந৅Խ w ඇಉظతͳॲཧ͸ͦΕࣗମ͕ঢ়ଶͱݴ͑Δ w ॲཧதͳͷ͔Ͳ͏͔ w ਖ਼ৗʹऴྃͨ͠ͷ͔ࣦഊͨ͠ͷ͔ w ϝΠϯεϨουΛࢭΊΒΕͳ͍

  33. ݴޠػೳͷར༻ w ίʔϧόοΫ w ؔ਺ϙΠϯλ w Ϋϩʔδϟ w HFOFSBUPS w

    BTZOD BXBJU
  34. ந৅දݱ w ΩϡʔΠϯά w 5ISFBE1PPM&YFDVUPS +BWB  w 0QFSBUJPO2VFVF J04

     w 1SPNJTF
  35. 3FBDUJWF1SPHSBNNJOH w 3Y+BWB "OESPJE  w 3Y4XJGU J04  w

    3FBDUJWF$PDPB J04  w 3Y+4 8FC
  36. 3Y+BWB val compositeDisposable = CompositeDisposable() fun createEntry(title: String, body: String)

    = Completable.create { emitter -> val entry = Entry(title = title, body = body, date = Date()) entryDao.insertEntry(entry) emitter.onComplete() } fun create() { val disposable = createEntry( "ࠓ೔ͷ೔ه", "ࠓ೔ͷ൩͝൧͸ϋϯόʔάͩͬͨɻ͓͍͔ͬͨ͠ɻ") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe() compositeDisposable.add(disposable) }
  37. ·ͱΊ

  38. (6*ΞϓϦέʔγϣϯͷಛੑ w Ϟσϧͷঢ়ଶΛ΋ͱʹ6*Λߋ৽͠ଓ͚Δ w ը໘ؒͰͷσʔλͷ΍ΓऔΓ͕ඞཁ w ϝΠϯεϨουΛࢭΊͯ͸͍͚ͳ͍

  39. ͭͷߏ଄ w 0CTFSWFSύλʔϯ w ΦϒδΣΫτͷੜ੒ɾ؅ཧ w ඇಉظॲཧͷந৅Խ

  40. "OESPJEBQQ ,PUMJO 3Y+BWB %BHHFS %BUB#JOEJOH "SDIJUFDUVSF$PNQPOFOUT

  41. "DUJWJUZ 'SBHNFOU 'SBHNFOU "QQMJDBUJPO "QQ
 $PNQPOFOU "QQ.PEVMF 7JFX.PEFM %BHHFS 3Y+BWB

    "SDIJUFDUVSF $PNQPOFOUT 0UIFS
 .PEFMT 7JFX 7JFX %BUB#JOEJOH "DUJWJUZ
 $PNQPOFOU "DUJWJUZ.PE
  42. 0CTFSWFSύλʔϯ w %BUB#JOEJOH w 3PPN "SDIJUFDUVSF$PNQPOFOUT  w -JWF%BUB "OESPJE

     w 3Y+BWB
  43. ΦϒδΣΫτͷੜ੒ɾ؅ཧ w %BHHFS w 7JFX.PEFM "SDIJUFDUVSF$PNQPOFOUT

  44. ඇಉظॲཧͷந৅Խ w Ϋϩʔδϟ ,PUMJO  w 3Y+BWB

  45. (6*ΞϓϦέʔγϣϯͷߏ଄ͱઃܭ w యܕతͳ(6*ΞϓϦέʔγϣϯͷઃܭ͸ͭͷߏ଄Ͱ
 આ໌Ͱ͖Δ w ͜ͷߏ଄ʹண؟͢Δ͜ͱͰෳࡶͳ(6*ΞϓϦέʔγϣϯΛ ͏·͘ઃܭͰ͖Δ