Architecture Blueprints の非同期処理実装にみる Android SDK の方向性

MVP、MVVM、Clean Architecture、Dagger2、Data Binding、Archtecture Components などいろいろな組み合わせの実装例が ToDoアプリにて公開されています。

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

非同期処理部分を見てみると現在はすべて(todo-mvp-rxjavaを除く)が以下の実装となり、非同期処理の主役であった AsyncTask/Loader API を利用した記述は消え去っています。

まず、java.util.concurrent.Executor(s) を使って、AppExecutors を作っておいて、


open class AppExecutors constructor(
    val diskIO: Executor = DiskIOThreadExecutor(),
    val networkIO: Executor = Executors.newFixedThreadPool(THREAD_COUNT),
    val mainThread: Executor = MainThreadExecutor()
) {

  private class MainThreadExecutor : Executor {

    private val mainThreadHandler = Handler(Looper.getMainLooper())

    override fun execute(command: Runnable) {
      mainThreadHandler.post(command)
    }
  }
}

AppExecutors.kt

それに対応するデータベースやストレージ向けのExecutorを作ります。


class DiskIOThreadExecutor : Executor {

  private val diskIO = Executors.newSingleThreadExecutor()

  override fun execute(command: Runnable) { diskIO.execute(command) }
}

DiskIOThreadExecutor.kt

これらを使って以下のようにして非同期処理を実装します。


appExecutors.diskIO.execute {

  // IOスレッドで実行する
  // ...

  appExecutors.mainThread.execute {

    // メインスレッドで実行する
    // ...

  }

}

実装例では、コールバックを使ってPresenterまで伝達しています。


override fun getTasks(callback: TasksDataSource.LoadTasksCallback) {

  appExecutors.diskIO.execute {

    // IOスレッドで実行する
    val tasks = tasksDao.getTasks()

    appExecutors.mainThread.execute {

      // メインスレッドで実行する
      if (tasks.isEmpty()) {
        callback.onDataNotAvailable()
      } else {
        callback.onTasksLoaded(tasks)
      }

    }

  }
}

TasksLocalDataSource.kt

AsyncTask/Loader APIs の排除の方向性は、「Deprecated(廃止予定) samples」に移動されたブランチからも認識できます。

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

この流れについては、droidcon NYC 2017 - Android Architecture Round Table でも、話が挙がっていました。

it looks like Google is abandoning old API is like loaders and recommending patterns there are much less coupled with the framework, which is good but what happens with these API is are we abundant in then and I'm talking about classes like sync adapters loader async tasks etc

Google はLoaderのような古いAPI や フレームワークと関係の薄いパターンを推奨することをやめているように見えます。 それはいいことですが、それら古いAPIを捨てることは何を引き起こすか、AsyncAdapter や Loaderなどについて話したいと思います。

今後の、Android SDKは、フレームワークを意識したAPIが増えていくのでしょう。


すばやく理解する「Room x RxJava 」

いい記事があったので。

Room 🔗 RxJava – Google Developers – Medium

まずは、Room で Dao.


@Query(“SELECT * FROM Users WHERE id = :userId”)
User getUserById(String userId);

ここまでで問題なのは、

1. 同期呼び出しでブロッキング。
2. データ変更時に再度呼び出す必要がある。

ということで、RxJava を使いたくなります。

Room は RxJava2.x に対応しています。

Adding Components to your Project | Android Developers

どのように使うのか?

Maybe


@Query(“SELECT * FROM Users WHERE id = :userId”)
Maybe<User> getUserById(String userId);

1. 該当ユーザがなければ、何も返さずに complete。
2. 該当ユーザがあれば、onSuccess となり complete。
3. Maybe が complete されたあとにユーザー情報が更新されても何もしない。

Single


@Query(“SELECT * FROM Users WHERE id = :userId”)
Single<User> getUserById(String userId);

1. 該当ユーザがなければ、何も返さず onError(EmptyResultException)。
2. 該当ユーザがあれば、onSuccess。
3. Single が complete されたあとにユーザー情報が更新されても何もしない。

Flowable


@Query(“SELECT * FROM Users WHERE id = :userId”)
Flowable<User> getUserById(String userId);

1. 該当ユーザはなければ、何も返さず emit もされない。当然、onNext も onError も呼ばれない。
2. ユーザが存在すれば、onNext。
3. ユーザ情報が更新されるたびに、自動で emit されるので、UI上を最新データに更新させることが可能になる。

 

まとめ

これだけ数行でデータベース、非同期処理を簡潔明快に説明できる Room x RxJava の組み合わせ。

おまけに Observable から細分化された RxJava2.x の主役たちの使い方も理解することができます。

素晴らしいですよね。


Java から Kotlin 移行の進捗を「cloc」で確認する

これを確認したかったのです.

特定のディレクトリのファイルやコード行数の確認.

googlesamples/android-architecture at todo-mvp-rxjava

「cloc」てやつを使います.

AlDanial/cloc: cloc counts blank lines, comment lines, and physical lines of source code in many programming languages.

こんな感じで結果表示されます.


for d in ./*/ ; do (cd "$d" && echo "$d" && cloc --vcs git); done

Android プロジェクトの場合, アプリのルートディレクトリでやると,


$ pwd
/Users/maochanz/projects/PlainTemplates
$ cloc .
     674 text files.
     567 unique files.
     140 files ignored.

github.com/AlDanial/cloc v 1.74  T=10.63 s (50.4 files/s, 8239.6 lines/s)
--------------------------------------------------------------------------------
Language                      files          blank        comment           code
--------------------------------------------------------------------------------
JSON                            253              0              0          30758
XML                             223           1749            233          17707
Java                             36             38          24757          10428
Kotlin                           18            245            324            993
Bourne Again Shell                1             19             20            121
DOS Batch                         1             24              2             64
Groovy                            3              8              3             55
Prolog                            1              3              0             18
--------------------------------------------------------------------------------
SUM:                            536           2086          25339          60144
--------------------------------------------------------------------------------

となって,

build ディレクトリもカウントされるので,

src ディレクトリ以下で実行するといいでしょう.


$ pwd
/Users/maochanz/projects/PlainTemplates/app/src
$ cloc .
      77 text files.
      76 unique files.
       1 file ignored.

github.com/AlDanial/cloc v 1.74  T=0.44 s (171.9 files/s, 7442.6 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
XML                             58            177             89           1463
Kotlin                          18            245            324            993
-------------------------------------------------------------------------------
SUM:                            76            422            413           2456
-------------------------------------------------------------------------------

Kotlin による冗長なコードの削減具合もよく分かるはずです.

CLOC -- Count Lines of Code