実践 Reactive Extensions

23
実践 Reactive Extensions 2011/08/27 Silverlight/Phone Quest I 伊勢 シン (Microsoft MVP for Windows Phone)

Transcript of 実践 Reactive Extensions

Page 1: 実践 Reactive Extensions

実践 Reactive Extensions

2011/08/27 Silverlight/Phone Quest I

伊勢 シン

(Microsoft MVP for Windows Phone)

Page 2: 実践 Reactive Extensions

自己紹介 • 伊勢 シン

– Microsoft MVP for Windows Phone (Oct 2010-Sep 2011)

• 大阪で働くスマートフォンプログラマ – 目指すは「スマートフォンの万屋」 – iOSアプリをビルドをしながらAndroidアプリのコーディングやったりとか

– Windows Phone 7 も少々

• 趣味もスマートフォンアプリ開発で – EbIRC

– SongTweeter

• コミュニティ – スマートフォン勉強会 代表

– Windows Phone Arch 関西スタッフ?

Page 3: 実践 Reactive Extensions

おしながき

• Reactive Extension について

–基本的なこと

–書き方の考え方

• 実例でみるReactive Extensions

– RSSの取得

– ログインが必要なページの先からファイルダウンロード

–複数ページをダウンロードして一括処理

–なんでもいいからとにかく非同期

Page 4: 実践 Reactive Extensions

Reactive Extensions について

Page 5: 実践 Reactive Extensions

そもそもReactive Extensionsって何ぞ

• Reactive Extensions (Rx)

• LINQの機能を時間軸に拡張するライブラリ

– LINQ to SQL, LINQ to Objects に対する

LINQ to Events, LINQ to Asynchronous

– “WhereでフィルタしてSelectで射影できるならLINQ

といえる” – by @neuecc

Page 6: 実践 Reactive Extensions

なにがうれしいの? • 複雑な非同期処理を「直線的に」書ける

– 非同期のコードはひたすらネストがつづく – “もはやカオスすぎて頭が痛い” by @neuecc

• 例外処理をひとまとめに書ける – Subscribeの第2引数のラムダで一括処理可能

• その他各種特典 – 中断処理も簡単。

– リトライ処理

– 処理の待ち合わせ、結合など

Page 7: 実践 Reactive Extensions

“もはやカオスすぎて頭が痛い” 例

Page 8: 実践 Reactive Extensions

そう、Rxならね

WebRequest.Create("http://hoge") .GetResponseAsObservable() .Select(res => new StreamReader(res.GetResponseStream()).ReadToEnd()) .SelectMany(s => WebRequest.Create(s).GetResponseAsObservable()) .Select(res => new StreamReader(res.GetResponseStream()).ReadToEnd()) .ObserveOnDispatcher() .Subscribe( s => MessageBox.Show(s), e => MessageBox.Show(e.ToString()));

Page 9: 実践 Reactive Extensions

どこでつかえるの?

• Windows Phone のクラスライブラリに 標準搭載され、非同期処理しか存在しない Silverlight の存在も相まって注目度アップ

• WP7だけでなく、いろいろなところでつかえます

– .NET Framework 3.5 / 4 Client Profile

– Silverlight 3 / 4 / for Windows Phone

– XNA 3.1(Zune) / 4 (Xbox360)

– http://www.microsoft.com/download/en/details.aspx?id=24940

Page 10: 実践 Reactive Extensions

どうやって使うの?@WP7

• 以下のアセンブリを参照

– Microsoft.Phone.Reactive

– System.Observable

• Rxを使いたいcsファイルのusingに以下を追加

– Microsoft.Phone.Reactive

• すべてのはじまりは Observable クラス

Page 11: 実践 Reactive Extensions

そもそもマルチスレッドの基本的な考え方

UIスレッド

トリガー

時間のかかる処理

どこか別のスレッド

結果の処理

スレッド同期

UI反映

Page 12: 実践 Reactive Extensions

Rx コードの基本構成

Observable.FromAsyncPattern<WebResponse>( req.BeginGetResponse, req.EndGetResponse)() .Select(res => XDocument.Load(res.GetResponseStream())) .SelectMany(doc => doc.Element("Categories").Elements("Category")) .Select(elem => new { ID = elem.Element("ID").Value, Name = elem.Element("Name").Value }) .ObserveOnDispatcher() .Subscribe(res => { // UI反映 }, err => { // 例外処理 });

1.トリガー

2.データ処理

3.スレッド同期

4.UI反映

Page 13: 実践 Reactive Extensions

簡単にRxを書くために

• 通信処理系のRxを書くなら

RxAsynchronousHelper を使うと便利です。

–通信を非同期に行うためのRxのおきまり処理を

うまくパッケージングしたクラス。あるだけで便利。

– by @neuecc

– http://neue.cc/2010/11/26_286.html

Page 14: 実践 Reactive Extensions

実例で見る

Reactive Extensionsの使い方

Page 15: 実践 Reactive Extensions

RSSを受信する

• 単純な処理の例

– XDocumentの名前空間つけないといけないのだけなんとかならんかと思っています。

Page 16: 実践 Reactive Extensions

単純なRSS受信

var req = WebRequest.CreateHttp("http://d.hatena.ne.jp/iseebi/rss"); Observable.FromAsyncPattern<WebResponse>(req.BeginGetResponse, req.EndGetResponse)() .Select(res => XDocument.Load(res.GetResponseStream())) .Select(doc => from elem in doc.Element(Name_RDF).Elements(Name_Item) select new { Title = elem.Element(Name_Title).Value, Link = elem.Element(Name_Link).Value }) .ObserveOnDispatcher() .Subscribe(res => { foreach (var item in res) { Debug.WriteLine("{0} -> {1}", item.Title, item.Link); } });

private const string Name_RDF = "{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF"; private const string Name_Item = "{http://purl.org/rss/1.0/}item"; private const string Name_Title = "{http://purl.org/rss/1.0/}title"; private const string Name_Link = "{http://purl.org/rss/1.0/}link";

Page 17: 実践 Reactive Extensions

RxAsynchronousHelperを使った場合

WebRequest.CreateHttp("http://d.hatena.ne.jp/iseebi/rss") .GetResponseAsObservable() .Select(res => XDocument.Load(res.GetResponseStream())) .Select(doc => from elem in doc.Element(Name_RDF).Elements(Name_Item) select new { Title = elem.Element(Name_Title).Value, Link = elem.Element(Name_Link).Value }) .ObserveOnDispatcher() .Subscribe(res => { foreach (var item in res) { Debug.WriteLine("{0} -> {1}", item.Title, item.Link); } });

Page 18: 実践 Reactive Extensions

ダウンロードの結果解析で次のファイルへ

• ログインが必要なページなどに有効

• SelectManyにすれば、流れに全く関係のないIObservable<T> を新規にメソッドチェーンに

流せる。

Page 19: 実践 Reactive Extensions

テキストに書いてあるURLをさらに取得

// URLが書いてある WebRequest.CreateHttp("http://iseebi.half-done.net/test/url.txt") .DownloadStringAsync() // 新しいIObservableを流す .SelectMany(res => WebRequest.CreateHttp(res).DownloadStringAsync()) .ObserveOnDispatcher() .Subscribe(res => { Debug.WriteLine(res); });

Page 20: 実践 Reactive Extensions

一括ダウンロードしてから処理

• 複数のファイルをダウンロードし、

ダウンロード完了後に処理したい場合

–定義ファイルの一括取得など

Page 21: 実践 Reactive Extensions

一括ダウンロード

// ダウンロード処理を一括でリストにする var l = new List<IObservable<string>>() { WebRequest.CreateHttp("http://iseebi.half-done.net/test/phone.txt").DownloadStringAsync(), WebRequest.CreateHttp("http://iseebi.half-done.net/test/clientappdev.txt").DownloadStringAsync(), WebRequest.CreateHttp("http://iseebi.half-done.net/test/deviceappdev.txt").DownloadStringAsync(), WebRequest.CreateHttp("http://iseebi.half-done.net/test/powershell.txt").DownloadStringAsync(), }; // ForkJoinで一括処理 Observable.ForkJoin(l) .ObserveOnDispatcher() .Subscribe(res => { foreach (var item in res) { Debug.WriteLine(item); } });

Page 22: 実践 Reactive Extensions

なんでもいいからとにかく非同期に

• とにかくなんでもいいので非同期にしたい場合、以下の組み合わせで何もないところから処理を作り出せる。

– Observable.Defer

– Observable.ToAsync

Observable.Defer(Observable.ToAsync(() => System.Threading.Thread.Sleep(3000))) .ObserveOnDispatcher() .Subscribe(res => Debug.WriteLine("Success!!!"));

Page 23: 実践 Reactive Extensions

まとめ

• Rx 使うと非同期処理が

きれい、かんたん、便利に書けます。

– いくつかの実例をみてご紹介しました。

• Special thanks to @neuecc