res を複数作って機能・画面別にディレクトリ分けすると分かりやすい

👉 Split layouts into subfolders in Android Studio - ProAndroidDev 

以下のコードを見ながら。

👉 GitHub - android/architecture-samples at dagger-android 

コードは、画面別にディレクトリ分けしてるが、レイアウトファイルは、複雑なファイル名で res ディレクトリ直下に一緒くたに入ってる。

ここで、build.gradle で設定記述を追加する。


android {
  sourceSets {
    main {
      res.srcDirs = [
          'src/main/res',
          'src/main/res/layouts/tasks',
          'src/main/res/layouts/taskdetail',
          'src/main/res/layouts/addedittask',
          'src/main/res/layouts/statistics',
      ]
    }

こうすることで、レイアウトファイル群を画面別に分けることができる。

Android Studio はデフォルトで、一つのディレクトリのみ


android {
  sourceSets {
    main {
      res.srcDirs = [
          'src/main/res'
      ]

と等価な設定を持ってるので、それに、画面別ディレクトリを複数追加することで、Android Studio は、リソースファイルとして複数ディレクトリからファイルを読み込むことができるようになります。

あとは、それぞれのリソースファイルを振り分けて置くだけ。

以下、やってみた動画。



実際は、レイアウトファイルがもっと多くなるはずなので、分かりやすさはより顕著になる。

👉 Split layouts into subfolders in Android Studio - ProAndroidDev 


さらば proguard、ようこそ R8。

DSL element 'useProguard' is obsolete and will be removed soon. Use 'android.enableR8' in gradle.properties to switch between R8 and Proguard..

DSL要素「useProguard」は廃止されており、まもなく削除されます。 gradle.propertiesで「android.enableR8」を使用して、R8とProguardを切り替えます。

gradle.properties


android.enableR8=true

build.gradle

とりあえずは、いけるが。

以下、編集風景。



しかし、minify とか、proguardFiles とか、どうなるの?

👉 Android Developers Blog: R8, the new code shrinker from Google, is available in Android studio 3.3 beta 


Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6. Please specify proper '-jvm-target' option

なんすかね、まったく。

Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6. Please specify proper '-jvm-target' option

[Preferences]-[Kotlin Compiler] から、[Target JVM version] を 「1.8」に。

ところが、同じエラーが出たまま。

対処法

以下を、build.gradle に。


android {
  kotlinOptions {
    jvmTarget = '1.8'
  }
}

Android Studio の便利そうに見える設定機能ってほとんどがあやしくね?

👉 intellij idea - Why kotlin gradle plugin cannot build with 1.8 target? - Stack Overflow 


Tracing the Origins of 'Presentation Model' in MVVM — and Martin Fowler’s Seven-Year-Old Insight

While researching things around “MVVM,” I kept seeing the term “Presentation Model,” so I decided to look into it.

Martin Fowler mentioned it — about seven years ago.


MVVM is the Microsoft's world's name for the same pattern as Presentation Model. I haven't updated the article since.

マイクロソフトの「MVVM」パターンは「Presentation Model」と同じなので、記事は更新していません。

👉 Martin Fowler on Twitter: "@HerberthAmaral MVVM is the Microsoft's world's name for the same pattern as Presentation Model. I haven't updated the article since." / Twitter 

I looked for the sources for each of them.

Presentation Model (Martin Fowler, 2004)

Represent the state and behavior of the presentation independently of the GUI controls used in the interface

👉 Presentation Model 

MVVM (Microsoft, 2005)

Model/View/ViewModel is a variation of Model/View/Controller (MVC) that is tailored for modern UI development platforms where the View is the responsibility of a designer rather than a classic developer. The designer is generally a more graphical, artistic focused person, and does less classic coding than a traditional developer.

👉 Introduction to Model/View/ViewModel pattern for building WPF apps – Tales from the Smart Client 

Conclusion

2012年4月現在、XAMLを使用するWPFなどのテクノロジ以外で使用されるMVVMは実質Presentation Modelと変わらず、Viewの抽象化などはできない。

👉 Model View ViewModel - Wikipedia (ja) 

MVVM is a variation of Martin Fowler's Presentation Model design pattern. MVVM abstracts a view's state and behavior in the same way, but a Presentation Model abstracts a view (creates a view model) in a manner not dependent on a specific user-interface platform.

👉 Model–view–viewmodel - Wikipedia (en) 


coroutine の使い方 - Android Architecture Blueprints v2 #1

気がついたら「v2」です。MVVM のみとなって、 RxJava は姿を消していますが。

👉 googlesamples/android-architecture: A collection of samples to discuss and showcase different architectural tools and patterns for Android apps. 



現在、4つのバリエーションが公開されていますが、以下は共通。


- kotlin
- coroutine
- single activity
- architecture component
- navigation component + fragment
- presentetion layer(per page) = fragment + view model
- reactive ui = live data + data binding
- data layer = repositpory + local(room) + remote
- datalayer one shot operations(no listener or data streams)

非同期処理には、どのバリエーションも「coroutine」使う。

どのように coroutine を使っているか、を定型化しておきたい。

関数 coroutineScope

「kotlinx-coroutines-core」の関数の「coroutineScope」 が目についたので見ておく。


suspend fun <R> coroutineScope(
    block: suspend CoroutineScope.() → R
): R (source)

CoroutineScope を作成し、このスコープで指定された suspend ブロックを呼び出します。その外側のスコープから coroutineContext を継承しますが、そのコンテキストのJobをオーバーライドします。

この関数は、処理の並列分解用に設計されています。 このスコープ内のいずれかの子コルーチンが失敗すると、このスコープは失敗し、残りのすべての子はキャンセルされます(supervisorScope との違いを参照してください)。 与えられたブロックとそのすべての子コルーチンが完了するとすぐに戻ります。
スコープの使用例は次のようになります。


suspend fun showSomeData() = coroutineScope {

  val data = async(Dispatchers.IO) { // <- extension on current scope
     // ... load some UI data for the Main thread ...
  }

  withContext(Dispatchers.Main) {
    doSomeWork()
    val result = data.await()
    display(result)
  }
}

この例のスコープの意味は次のとおりです。

1. showSomeDataは、データがロードされてUIに表示されるとすぐに戻ります。
2. doSomeWorkが例外をスローすると、非同期タスクはキャンセルされ、showSomeDataはその例外を再スローします。
3. showSomeData の外部スコープが取り消されると、開始された async ブロックとwithContext ブロックの両方が取り消されます。
4. 非同期ブロックが失敗すると、withContext はキャンセルされます。

現在のジョブが外部でキャンセルされた場合、メソッドは CancellationException をスローします。このスコープ内に未処理の例外がある場合(たとえば、このスコープ内の起動で開始されたクラッシュコルーチンから)は、対応する未処理のThrowableをスローします。

coroutineScope - kotlinx-coroutines-core

実際の記述

coroutine を使った非同期処理は 各 ViewModel を起点に記述されています。


class TasksViewModel(
    private val tasksRepository: TasksRepository
) : ViewModel() {

    fun loadTasks(forceUpdate: Boolean) {
        viewModelScope.launch {

            // 対応する Repository メソッドをコール
            // val tasksResult = tasksRepository.getTasks(forceUpdate)

        }
    }

}

👉 android-architecture/TasksViewModel.kt at master · googlesamples/android-architecture 

Repository を展開して、スコープ周りを抜き出してみると、


class TasksViewModel(
    private val tasksRepository: TasksRepository
) : ViewModel() {

    fun loadTasks(forceUpdate: Boolean) {
        viewModelScope.launch {

            withContext(ioDispatcher) {
            
                // ...            

            }

        }
    }

}

というこれまで通り withContext() を使った dispatcher の切り換え。

他には、

前述の coroutineScope 関数を使った記述が、単独、並列、入れ子の3種類。


override suspend fun saveTask(task: Task) {
    coroutineScope {
        launch { tasksRemoteDataSource.saveTask(it) }
        launch { tasksLocalDataSource.saveTask(it) }
    }
}


override suspend fun clearCompletedTasks() {
    coroutineScope {
        launch { tasksRemoteDataSource.clearCompletedTasks() }
        launch { tasksLocalDataSource.clearCompletedTasks() }
    }
    withContext(ioDispatcher) {
        cachedTasks?.entries?.removeAll { it.value.isCompleted }
    }
}


override suspend fun deleteAllTasks() {
    withContext(ioDispatcher) {
        coroutineScope {
            launch { tasksRemoteDataSource.deleteAllTasks() }
            launch { tasksLocalDataSource.deleteAllTasks() }
        }
    }
}

👉 android-architecture/DefaultTasksRepository.kt at master · googlesamples/android-architecture 

今後、さらに簡潔に記述されていくのだろうが、意図は今のが理解しやすいと思いメモ。