実践 reactive extensions
TRANSCRIPT
実践 Reactive Extensions
2011/08/27 Silverlight/Phone Quest I
伊勢 シン
(Microsoft MVP for Windows Phone)
自己紹介 • 伊勢 シン
– Microsoft MVP for Windows Phone (Oct 2010-Sep 2011)
• 大阪で働くスマートフォンプログラマ – 目指すは「スマートフォンの万屋」 – iOSアプリをビルドをしながらAndroidアプリのコーディングやったりとか
– Windows Phone 7 も少々
• 趣味もスマートフォンアプリ開発で – EbIRC
– SongTweeter
• コミュニティ – スマートフォン勉強会 代表
– Windows Phone Arch 関西スタッフ?
おしながき
• Reactive Extension について
–基本的なこと
–書き方の考え方
• 実例でみるReactive Extensions
– RSSの取得
– ログインが必要なページの先からファイルダウンロード
–複数ページをダウンロードして一括処理
–なんでもいいからとにかく非同期
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
なにがうれしいの? • 複雑な非同期処理を「直線的に」書ける
– 非同期のコードはひたすらネストがつづく – “もはやカオスすぎて頭が痛い” by @neuecc
• 例外処理をひとまとめに書ける – Subscribeの第2引数のラムダで一括処理可能
• その他各種特典 – 中断処理も簡単。
– リトライ処理
– 処理の待ち合わせ、結合など
“もはやカオスすぎて頭が痛い” 例
そう、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()));
どこでつかえるの?
• 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
どうやって使うの?@WP7
• 以下のアセンブリを参照
– Microsoft.Phone.Reactive
– System.Observable
• Rxを使いたいcsファイルのusingに以下を追加
– Microsoft.Phone.Reactive
• すべてのはじまりは Observable クラス
そもそもマルチスレッドの基本的な考え方
UIスレッド
トリガー
時間のかかる処理
どこか別のスレッド
結果の処理
スレッド同期
UI反映
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反映
簡単にRxを書くために
• 通信処理系のRxを書くなら
RxAsynchronousHelper を使うと便利です。
–通信を非同期に行うためのRxのおきまり処理を
うまくパッケージングしたクラス。あるだけで便利。
– by @neuecc
– http://neue.cc/2010/11/26_286.html
実例で見る
Reactive Extensionsの使い方
RSSを受信する
• 単純な処理の例
– XDocumentの名前空間つけないといけないのだけなんとかならんかと思っています。
単純な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";
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); } });
ダウンロードの結果解析で次のファイルへ
• ログインが必要なページなどに有効
• SelectManyにすれば、流れに全く関係のないIObservable<T> を新規にメソッドチェーンに
流せる。
テキストに書いてある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); });
一括ダウンロードしてから処理
• 複数のファイルをダウンロードし、
ダウンロード完了後に処理したい場合
–定義ファイルの一括取得など
一括ダウンロード
// ダウンロード処理を一括でリストにする 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); } });
なんでもいいからとにかく非同期に
• とにかくなんでもいいので非同期にしたい場合、以下の組み合わせで何もないところから処理を作り出せる。
– Observable.Defer
– Observable.ToAsync
Observable.Defer(Observable.ToAsync(() => System.Threading.Thread.Sleep(3000))) .ObserveOnDispatcher() .Subscribe(res => Debug.WriteLine("Success!!!"));
まとめ
• Rx 使うと非同期処理が
きれい、かんたん、便利に書けます。
– いくつかの実例をみてご紹介しました。
• Special thanks to @neuecc