「Fragment を利用するか、しないか」という話

How often do you actually use fragments in your job? : androiddev

「どれくらいの頻度でFragmentを使っていますか。」というスレ。

興味深いコメントを多く見ることができます。

以前からFragmentについては否定的な意見を続けている Jake Warthon さんも登場しています。

5 years sober!

5年間使っていない。

Fragments don't represent anything overwhelmingly challenging. Creating re-usable pieces of UI tied to reusable controller/presenter/whatevers doesn't even require a library. Their problem stems from a lifecycle that's too complicated compounded with a menu system that's convoluted and obsolete compounded with trying to solve dialogs, UI, and retaining instances across rotation compounded with asynchronous transactions compounded with an opaque backstack.

Fragment はチャレンジするものではありません。再利用可能なUIのパーツ作成には、再利用可能なコントローラやプレセンターなどを使えば他のライブラリなど必要ありません。問題なのは、複雑すぎるメニューの仕組みとライフサイクルが根源で、不透明なバックスタックを抱えた非同期トランザクションを組み込んでおり、ローテーション時のインスタンス保持をダイアログやUIに対して解決しようとして複雑になりすぎています。

All of these things should have been decoupled: creating modular bits of UI + code with a simple lifecycle, navigation between conceptual destinations, workers that are retained across rotation, and multiple sources of contribution to a menu.

以下のように分離すべきです。

- シンプルなライフサイクルをもつ小分けされた UI+コード
- 概念的な遷移先への遷移
- ローテーション時のワーカーの保持
- メニューに必要な複数のソース

また、「別Activityに遷移する場合は起動に時間がかかる」ということに関しては以下のように答えています。

I only use a single activity. Activity transitions are a mess. I've never seen one that didn't jank or get screwed up by the result of scrolling or adapter changes behind the scenes. I'll use a second activity if there are vastly different window styles such as a pop-up as the result of a notification vs. the full-screen app. Granted, transitions in the same window are also a mess since the use of view overlay means elevation doesn't work... but at least when everything is in one window you actually have control of things.

一つの Activity とします。Activityのトランジションはややこしいです。スクロール処理やアダプターの変更結果で悩まされたり苦労してないものを見たことがありません。

通知の結果としてのポップアップやフルスクリーンのアプリなど、ウィンドウスタイルが大きく異なる場合は、2番目のアクティビティを使用します。 確かに、同じウィンドウ内での遷移は、表示オーバーレイ時にエレベーションが機能しないので混乱しますが、少なくともすべてが1つのウィンドウ内にあるときは、実質的には処理できます。

I've been doing it this way for the last 5 years. No problems. Or rather, all the problems were problems with how the screen was implemented and not with the mechanism itself. The fine grained control shouldn't be at the site requesting navigation anyway. It's either data in the arguments of the navigation action or an implementation of whoever is implementing the mechanism of animation between screens. Neither belongs inside the call site of screen initiating navigation nor the destination screen.

私は、このやり方で5年間やってきましたが何の問題もありません。メカニズム自体が問題になることはなく、むしろ、すべての問題は画面の実装方法によるものです。とにかく、遷移をリクエストする側にきめ細かいコントロール処理を置いてはいけません。遷移のアクションかアニメーションどちらかの利用する引数データだけを置きます。これらは、遷移元、遷移先のどちらにも属しません。

具体的にこんなやりとりもあります。

I'm not sure how he handles viewpager without fragments but for all other purposes he mostly uses custom views (at least that's what I've found from his github)

FragmentなしでViewPagerの使い方がわかりません。他はカスタムViewでやっています。

View pager.

「View」の Pager ですよ。

ネット上にもFragmentを利用したサンプルばかりが目に付きますが、

結局、Fragmentを使う理由は、

「Fragmentを使わない実装方法が分からない、分かりづらい」

というのが実質的なところでしょうか。


JetBrains の IDE が全て半額! 急げ!! 当然 Intellij IDEA もだぞ?

急げ! あと数時間しかないぞ!

Don’t miss the JetBrains Friendship Day special offer — all individual tools for developers at 50% off! #JetBrainsFriends

持ってる知り合いを探して、クーポン発行をお願いする、なのか?!

詳細はよう分からんがお得なはず!!


Kotlin で FizzBuzz

考え方や汎用性など落とし所をどうするか。

方針:
- 「15」はコードに入れない。
- 表示処理の分離(MVP)を考慮する。
- 文字列の結合には StringBuilder を使う。


(1..100).map { 

  // P
  StringBuilder().apply {
    if (it % 3 == 0) append("Fizz")
    if (it % 5 == 0) append("Buzz")
    if (isEmpty()) append(it)
  }.toString()

}.forEach {

  // V
  println(it)

}


1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
...

問う側のスペックを問うブーメラン含みな相性判定なんだろうな。

むしろ方針?

👉 Kotlin で FizzBuzz の適当な記述 
あみだくじを罫線で書く
Fizz buzz - Wikipedia


Related Categories :  AndroidDevelopmemt


【Android】Kotlin でモダンな concurrency その5

ライフサイクルとコルーチン

Actor は、UI管理にも便利で、タスクのキャンセルをシンプルにし、UIスレッドのオーバーロードを避けることができます。

まず、Activity に適用する JobHolder インターフェースを作成します。これは、セットしたタスクの親となり、それのキャンセルを可能にします。


interface JobHolder {
  val job: Job
}

Activity が destroy されるときに、job.cancel() を行います。


class MyActivity : AppCompatActivity(), JobHolder {

  override val job: Job = Job() // the instance of a Job for this activity

  override fun onDestroy() {
    super.onDestroy()
    job.cancel() // cancel the job when activity is destroyed
  }
}

Extension Function にして、JobHolder の すべての View からアクセス可能にします。


val View.contextJob: Job
  get() = (context as? JobHolder)?.job ?: NonCancellable

これらを組み合わせて、setOnClick に onClick のアクションを管理させるための conflated な Actor を作らせます。複数回の連続クリックは無視され、ANR を避けることができます。

そして、これらのアクションは、contextJob のコンテキストで実行されます。

また、Activity が destroy されるとキャンセルもされます。


fun View.setOnClick(action: suspend () -> Unit) {
  val eventActor = actor<Unit>(
    context = UI,
    start = CoroutineStart.UNDISPATCHED,
    capacity = Channel.CONFLATED,
    parent = contextJob
  ) {
    for (event in channel) action()
  }
  setOnClickListener { eventActor.offer(Unit) }
}

この例では、ここでは多すぎるイベントを無視するために Channel を conflated としてセットしています。すべてをイベントキューとしたい場合は、Channel.UNLIMITED とすることができます。その場合でも ANR は発生しません。

コルーチンとライフサイクルを組み合わせて、UIタスクのキャンセルを自動化することもできます。


val LifecycleOwner.untilDestroy: Job get() {
  val job = Job()

  lifecycle.addObserver(object: LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy() { job.cancel() }
  })
  return job
}

// 使い方
launch(UI, parent = untilDestroy) {
  // 何らかの処理
}

【Android】Kotlin でモダンな concurrency その1
【Android】Kotlin でモダンな concurrency その2
【Android】Kotlin でモダンな concurrency その3
【Android】Kotlin でモダンな concurrency その4


【Android】Kotlin でモダンな concurrency その4

Channel を使ってコールバック不要に

Channel の定義 (JetBrains のドキュメントより):

Channel は、概念的に BlockingQueue とよく似ています。主な違いの一つは、Put の代わりに「送信中断」をを持ち、Take の代わりに「受信中断」を持つことです。

Blocking Queues

Actor

Channel をシンプルに使えるツールが Actor です。

Actor は Handler と非常によく似ており、コルーチンのコンテキスト(つまり、アクションを実行するスレッド)を定義し、シーケンシャルに実行します。

コルーチンを使っており、キャパシティを決めて実行を中断することができます。

Actor は基本的に、処理をコルーチンチャンネルに転送します。実行順序と実行するコンテキストを限定することを保証します。

これで、synchronize は不要となり、すべてのスレッドはフリーです。


protected val updateActor by lazy {
  actor<Update>(UI, capacity = Channel.UNLIMITED) {
    for (update in channel) when (update) {
      Refresh -> updateList()
        is Filter -> filter.filter(update.query)
        is MediaUpdate -> updateItems(update.mediaList as List<T>)
        is MediaAddition -> addMedia(update.media as T)
        is MediaListAddition -> addMedia(update.mediaList as List<T>)
        is MediaRemoval -> removeMedia(update.media as T)
    }
  }
}

// 使い方
suspend fun filter(query: String?) = updateActor.offer(Filter(query))

この例では、実行するアクションを選択する際、sealed クラスを利用しています。


sealed class Update
object Refresh : Update()
class Filter(val query: String?) : Update()
class MediaAddition(val media: Media) : Update()

すべてのアクションはキューとなり、決してパラレルには実行されません。mutable なものを密閉するにはいい方法です。

(つづく)

【Android】Kotlin でモダンな concurrency その1
【Android】Kotlin でモダンな concurrency その2
【Android】Kotlin でモダンな concurrency その3