【和訳】LiveData with Coroutines and Flow (Android Dev Summit '19)

自分用にgoogleで翻訳

こんにちは、私の名前はJOSE ALCERRECAです。私はAndroidで作業する開発者リレーションエンジニアです。
私の名前はYIGIT BOYARです。Androidチームでも働いています。
そして今日は、LiveDataについて再び話します。
私たちは昨年、LiveDataで楽しいというタイトルの講演を行いました。
今日は、CoroutineおよびFlowとの統合について説明します。
APIはAndroidのライフサイクルを理解するのに苦労したためです。
私たちは皆、ダイアグラムとより多くのダイアグラムを研究しました。その後、フラグメントがやって来て、より多くのダイアグラムを生成しました。
そのため、Android開発者は、この問題に対処する必要から自分自身を隔離しようとします。
プレゼンテーションレイヤーのみがライフサイクルを認識できるレイヤーアーキテクチャを作成し、プレゼンテーションレイヤー内でも、たとえばアクティビティやフラグメントのレクリエーションを生き残るオブジェクトを持つようなパターンやルールを見つけました。
このアイデアは、アーキテクチャコンポーネントのビューモデルクラスで使用します。
それでは、そのアニメーションをもう少し詳しく見てみましょう。
そのため、ビューが作成され、ビューモデルが取得されます。
同様に作成されます。
1つまたは複数のLiveDataを公開し、ビューはデータバインディングまたは手動サブスクリプションを介してこれらのLiveDataにサブスクライブします。
次に、構成変更のローテーションなどがあります。
ビューが破棄され、ビューの新しいインスタンスが表示されます。
LiveDatasに再度サブスクライブし、ビューモデルは何が起こったのかを知りません。
ビューモデルはデータへの公開のみであるため、ビューへの参照すらありません。
なぜこれについて話しているのですか。
操作を行うと、操作はネットワークからデータを取得するようなものになり、テキストを準備するたびに、この操作の範囲を選択する必要があります。
これは、この操作がいつキャンセルされるかを意味します。
キャンセルが遅すぎると、リソースの使用が無駄になる可能性があります。キャンセルが早すぎると、操作を再開する必要がある場合があります。
しかし、実世界のアプリでは、これがAndroid Dev Summitアプリであり、それ以上の範囲があります。
たとえば、フラグメントの複数の潜在的に複数のインスタンスを含むスケジュール画面には、画面にスコープが設定されたビューモデルと、フラグメントとビューモデルの情報画面も含まれます。
次に、メインアクティビティと、これらのアクティビティをスコープとするビューモデルがあります。
ナビゲーションコンポーネントでカスタムスコープを持つこともできます。たとえば、ログインフローまたはチェックアウトフローのスコープを作成できます。アプリケーションスコープもありますが、これについては後で説明します。
同時に多くの操作が同時に発生するため、それらのキャンセルを何らかの方法で管理する必要があります。
この並行性を構築するのに役立つ何かを見つける方法が必要です。

それは何でしょうか。
もちろんこれ。
コルーチンと呼ばれる新しい親友。
ここでは、Kotlinコルーチンを紹介して紹介したくありませんが、Android開発のこの特定のユースケースでそれが好きな理由についてお話したいと思います。
簡単に言うと、3つの主な利点があります。
1つ目は、メインスレッドからの離脱が非常に簡単なことです。
このような問題は、20種類の技術のように解決しようとしました。
Coroutineで起動したばかりで、ディスパッチャを変更できるので、非常に簡単に記述できます。
そして、ボイラープレートコードを記述する必要がないことを書いている間、これはおそらくKotlinコルーチンについて最高のことです。
ブロッキングコードを記述しているように記述します。
そして、それはすべてコンパイラの魔法であり、非同期になります。
これら2つは素晴らしいプロパティですが、このケースでコルーチンが好きな理由ではありません。
主な理由は、それが提供する構造化された並行性です。
少し複雑ですが、基本的には、構造の同時実行をジョブのガベージコレクターと考えることができます。
そのため、何かを実行する必要がなくなると、自動的に殺されます。
これが私たちがそれを好む理由です。
AndroidのCoroutineが気に入ったら、どのように起動しますか。
幸いなことに、Jetpackコンポーネントでは、関連するAndroidコンポーネントのスコープを提供しています。
たとえば、ビューモデルを使用していて、ビューモデルのスコープで何かを実行したい場合、それを使用してコルーチンを起動できるビューモデルスコープがあります。
または、アクティビティまたはフラグメントにいる場合は、ライフサイクルが破壊されるとキャンセルされるライフサイクルスコープを使用できます。
このライフサイクルの場合、もう1つあります。
ライフサイクルがフラグメントトランザクションのように開始されたときに特定の操作を実行したい場合があります。
そのような場合、再開または作成されたメソッドの開始時にこの起動を使用できます。
これは、ライフサイクルが望ましい状態以上にある場合にのみ、サスペンドログをスローします。
そして、起動時に起動を開始し、アクティビティが停止したと言った場合、アクティビティが再開するまでその計算を一時停止します。
ここで、3番目のレイヤーはアプリケーションレイヤーであり、どのスクリーンにも結び付けられていないものを実行することを前提とするなど、少し複雑になります。
そのような場合、ワークマネージャーを使用する必要があるかどうかを判断する必要があります。
そしてあなたが尋ねる質問は、このことを完了する必要があるということです。
Twitterクライアントを作成していて、サーバーに送信する必要がある場合は、ワークマネージャーを使用してください。
ただし、ローカルデータベースをクリーンアップする場所で何かを実行しようとしている場合は、アプリケーションスコープを使用できます。
それが完了しなかったとしても、それは世界の終わりではないので、次にアプリケーションを起動したときにそれを行うことができます。
次に、LiveViewでそのビューモデルスコープを使用する方法を見てみましょう。
ここに、UIを実行するためのパブリックプロパティとして提供するプライベートな可変LiveDataがある一般的なパターンがあります。
ここで、改造されたスコープを起動し、計算を一時停止し、結果が出たらプライベートな可変LiveDataを更新します。
そして、これはデフォルトでメインディスパッチャで実行されるため、post valueを呼び出していない値を設定しているだけであることに注意してください。
ライフサイクル2.2以降では、LiveDataと呼ばれる新しいビルダーであるこのパターンを実行するためのはるかに優れた方法があります。
このLiveDataビルダーは、LiveDataのスコープのようなCoroutineブロックを提供します。
LiveDataが観察されたときに実行を開始し、LiveDataが使用されなくなった場合や、その内部でEmitを実行できる場合はキャンセルされます。
別の非常に一般的なパターンは、ユーザーがいくつかのアイテムを選択し、そのコンテンツを表示するUIがあると想像するようなものです。
実際の一般的な方法は、そのアイテムIDを変更可能なLiveDataに保持し、その上でスイッチマップ変換を実行することです。
これらの場合にも、このLiveDataビルダーを使用して、コルーチン計算を実行できます。
このLiveDataビルダーはCoroutineコンテキストも受け取るため、デフォルトでI / Oで実行するために呼び出すことができます。
Emitを呼び出すことができるかどうかは関係ないので、Emitをいつでも呼び出すことができるスレッドまたはディスパッチャーについて考える必要はありません。
Emit sourceというもう1つの関数があり、これはLiveDataを受け取ります。これは基本的に、この他のLiveDataディスパッチが値として送信することを伝えます
キャンセルする方法を教えてください。


コルーチンをどのようにキャンセルしますか？
さて、私はあなたが始めたコルーチンを実際にキャンセルする方法について話すためにここにはいません。
コルーチンをスコーピングして、これが自動的に行われるようにすることについて話してきました。
これにはいくつかの使用例がありますが、通常は自分のジョブをキャンセルすることはありません。
ここで指摘したいのは、サスペンド関数を設計していて、たとえばその関数に無限ループがある場合に起こることです。
Kotlinは、ループをいつ停止するかをどのようにして知るのですか
ここにはちょっとした魔法があります。
KotlinXコルーチンのすべての支出機能はキャンセル可能で、遅延もその1つです。
そのため、遅延が呼び出されると、コルーチンがキャンセルされたかどうかが確認され、キャンセルされた場合は実行が停止されます。
したがって、サスペンド関数でキャンセル可能な関数を呼び出さないとどうなりますか。
キャンセルは協力的だと言ってください。
is activeプロパティを使用して、コルーチンが定期的にアクティブになっているかどうかを確認する必要があります。
そのため、いくつかのボタンを見る前に、重要な区別があります。
それは、ワンショット操作と、時間の経過とともに複数の値を返す操作の間にあります。
これについては、Twitterアプリの例を使用して説明します。
そのため、この画面に必要なデータをロードするには、いくつかのワンショット操作を実行する必要があります。
プロフィール画像をダウンロードすると、Twitterはツイート自体を処理します。2019年にツイートは編集できないためです。
しかし、リツイートの最下位の数には、多くのいいね！の興味深いものがあります。
このUIがデータソースを監視しているのは、この画面を開いたままにすると、これらの数値が時間とともに変化するためです。
それが違いです。
全体像では、ワンショット操作はどのように見えますか。
冒頭で説明した理由により、voモデルとBモデル間で同様のデータを使用します。
次に、ビューモデルで、LiveData Coroutine Builderを使用して、LiveDataワールドとCoroutineワールドの間にこのブリッジを作成します。
一時停止機能を使用しているので、データソース内の一時停止機能を呼び出すだけで、結果が得られます。
複数の値を処理する必要がある場合、事態は複雑になります。
これを解決するために、LiveDataを使用してこれを解決できます。ビューモデルを超えてLiveDataを使用できます。昨年の講演の一部はこれについてでした。
しかし、LiveDataは完全なリアクティブストリームビルダーとして設計されたことはないため、使用するのは少し厄介です。
幸運なことに、flowという新しいAPIがあります。
フローはKotlinコルーチンの一部であり、いくつかのコード例のいくつかの例を見て、ビューモデルリポジトリとデータソースを見ていきます。
まず、ビューモデルのパターンをいくつか見て、2つのビューモデルを並べて比較します。
1つはLiveDataをサブスクライブするか、LiveDataからデータを消費し、もう1つはフローから消費します。
最初のパターンは、複数の値を放出することです。
値への変換を行っていない場合は、監視しているLiveDataを、公開しているLiveDataに割り当てるだけです。
それはとても簡単です。
フローの場合、フローからLiveDataに何らかの形で変換する必要があります。したがって、LiveData Coroutine Builderを使用できます。
そのため、フローを消費する方法であるフローから各アイテムを収集し、すべてのアイテムをLiveData Coroutine Builderに送信します。
これは多かれ少なかれ読みやすいですが、ビューモデルでこれを15回実行しなければならない場合、多くの決まり文句になります。
そのため、フローをLiveDataに変換するLiveData拡張機能としてこの便利な機能を作成しました。
残りの例で使用します。
次のパターンは、1つの初期値を放出してから複数の初期値を放出します。
したがって、LiveData Coroutine Builderからの発行および発行ソースを使用したこのパターンは既に見ました。
フローでは、同じパターンを使用できます。
そして、asLiveDataでLiveDataに変換されたフローを渡します。
しかし、これはあまり読みやすくなく、非常に厄介です。LiveDataを2回作成しています。
したがって、フローAPIを採用し、開始時に呼び出されるこのフローを使用すると、そのフローの初期値を定義できます。
そして最後に、LiveDataに変換するだけです。
そして、これはフローが本当に本当に輝く場所です。あなたが観察している、または受け取っている各アイテムに変換をしたいのです。
LiveDataを使用してこれを行う場合、最初にマップを試してみることをお勧めしますが、これは主な脅威であり、ここからコルーチンを使用するのは厄介です。
そのため、スイッチマップを使用し、ビルダーで作成されたLiveDataを返す方がはるかに優れています。
そして、私たちはコルーチンコンテキストにいるので、この重い変換関数を呼び出すことができます。これは一時停止関数になり、その結果を出力します。
フローでは、フローでmapを呼び出すだけで、はるかに改善されます。
そして、コルーチンとコンテキストであるため、最後にサスペンド関数を呼び出すことができます。LiveDataに変換するだけです。


大丈夫。
リポジトリでサスペンド関数とフローライブラリをどのように使用しますか。
現在、リポジトリでは通常、多くのカスタム呼び出しが行われます。
APIを作成しているように、あなたが持っているビジネスをフィルタリングしてデータを収集しています。
これらの場合、多くの変換を行いたい場合があります。ホセが述べたように、LiveDataがこれらのことを行うことを意図していませんでした。
幸いなことに、フローにはこれらのすべての演算子がストリームにあり、使用できます。
リポジトリでフローまたはサスペンド機能を使用してください。
同様に、フロー上のデータソースのサスペンド関数は非常に適していますが、ここでは他のライブラリのリモートソースと通信しているコードの一部であるため、少し物事が複雑になります。
そのため、すべてを制御するわけではありません。
これらすべてのケースを見て、どのように統合できるか見てみましょう。
最初のケース最良のケースは、シャットダウン操作が1つあることです。
そのため、ネットワーク要求から言われるような値を取得するためのワンショット操作があります。
そのため、Retrofitを使用している場合は、機能を一時停止としてマークするだけです。
レトロフィットはすでにソースパン機能をサポートしており、バージョン2.6をサポートしています。
同様に、バージョン2.1以降のデータベースでRoomを使用している場合、サスペンド機能と見なされます。
そのため、サスペンド機能を終了するように指示します。もう気にする必要はありません。
しかし、コルーチンの世界用に更新されていないライブラリを扱っている場合、それは一般的なライブラリであり、コールバックを持っている可能性があります。
とても古いコールバックではありません。
今、あなたはいくつかの仕事をする必要があります。
そのような場合、中断可能なキャンセル可能なコルーチンビルダーと呼ばれるものがあります。
これは基本的に、コルーチンの世界とコールバックベースの世界との間のアダプターです。
あなたがすることは、この関数を呼び出すとき、あなたが呼び出す責任があるという継続を与えます。
したがって、APIが継続を再開する値を提供する場合、APIリクエストを行います。
または、APIがエラーの発生を通知した場合は、例外を使用して継続を再開します。
これを行うと、それが機能するコルーチンミッションを解決し、コードのどこかでこれを抽象化できます。
キャンセルされた後など、この継続をたまたま呼び出した場合、無視されることに注意してください。
また、コルーチンがキャンセルされた場合、APIリクエストを積極的にキャンセルできるように、変更も少し改善されています。
3番目のケースは、複数の値を出力したいような値がたくさんある場合です。
Flow Builderはすばらしいことで、LiveData Builderと非常によく似ていますが、Flowを作成して値を出力できる点が異なります。
したがって、ここでは、2秒ごとに無限に新しい天気値をディスペンスする例です。
だよ。テストを実際に書いているときにも非常に便利です。
これがこの別のフローであると言うだけです。
ただし、フローをサポートせず、コールバックである古いライブラリを再度使用している場合は、このコールバックフロービルダーを使用できます。
以前見たものと非常に似ています。
そのため、ここには架空のコールバックを受け取る架空のAPIがあり、そのコールバックには3つの関数があります。
エラー時および完了時の次の値です。
架空のAPIでそのコールバックを登録します。これらのコールバックの1つを受け取るたびに、その値を提供するだけです。
電子メールがフローに提供するか、エラーが発生した場合。
基本的にこのエラーでフローを閉じるか、フローを閉じるのと同じ方法でうまく閉じる場合。
ここで注意が必要なのは、このコールバックFlow BuilderがFlowを返すと閉じられ、値をディスパッチできなくなるためです。
戻りたくありません。
そのため、これらのケースでは、待機呼び出しと呼ばれる関数と呼ばれるものがあり、基本的にフローがデータを収集しなくなるまで一時停止します。
ここで閉じた理由があるか、コレクターが収集を停止した可能性があります。
その場合、APIから登録解除するだけです。

テスト中です。テストについてお話したいと思いますが、それほど時間はありません。
いいえ、時間はあります。
テストは重要なので、明日5:15にショーンとマニーによるテストに関する本格的な講演を行います。
テストで品質を効果的に使用する方法を確認してください。
出発する前にもう一つあります。
Flowとライフサイクルスキルの紹介により、LiveDataを使用せずにLiveDataのメリットを実際に得ることができることを示しました。
そのため、ライフサイクルスコープが起動時に起動し、このフローで収集を開始するということを行うことができます。
これは、LiveDataのUIの動作とまったく同じように動作します。
LiveDataにはこのキャッシュ動作があるため、100％同じではありませんが、近いところにあります。
だから、あなたが考慮したいことがあると言っています。
質問がある場合は、2階になります。