今回のエンジニアブログ担当の岩原です。
今回は、「MacでC#を書いてみよう(GUI編)後編」です。
初めて見る方は、前回の記事を先にご覧ください。
Xamarin Studioがメジャーバージョンアップし、F#がつかえるようになりましたね。
アップデートしておきましょう。
では、前回の続きです。
まずはお天気Webサービス仕様 - Weather Hacks - livedoor 天気情報を開き、JSONの仕様を確認します。
次に、ここを開き、
自分の住んでいる地域のcityのidを確認します。
今回は名古屋の天気を取得します。名古屋は230010です。
したがって、名古屋の天気をJSONで取得する場合、
http://weather.livedoor.com/forecast/webservice/json/v1?city=230010
になります。
今度は、JSON文字列をクラス化します。
愚直にクラス化すると面倒なので、json2csharp - generate c# classes from jsonを使用します。
JSON文字列をコピーし、「Generate」ボタンをクリックすれば、クラス化されたソースが取得できます。
ここで取得したソースとJson.NETを使えば、お手軽にパースできます。
まずは、生成されたソースをコピーします。
コピーしたソースをC#ファイルとして貼り付けます。
RootObjectではわかりづらいので、クラス名をWeatherInfoに変更します。
C#では、プロパティの先頭は基本的に大文字にすべきなので、WeatherInfoと他のクラスのプロパティもすべて先頭大文字に変更します。
次にボタンのイベントを作成します。
デザイナタブをクリックしてデザイナモードにします。
ここで画面右側にプロパティが表示されていない場合、Visual Designを表示させます。
画面上部のビュー→Visual Designの順にクリックすると表示されます。
その後、ボタンを選択、シグナルタブの「Button Signals」を展開し、「Clicked」に「onbtnGetList」と入力します。
すると、エディタにメソッドが追加されているので、それに処理をゴリゴリ書いていきます。
では、まずは取得処理。
JSONのURLは定数化しておきましょう。
取得処理は別メソッド化します。
このメソッドは別スレッドから呼ばれるメソッドになるため、画面部品は触らないようにします。
private String GetWheatherInfo () { //JSON_URLはJsonを取得するURL定数 var req = WebRequest.Create (JSON_URL); var res = req.GetResponse (); using (Stream st = res.GetResponseStream ()) { using (StreamReader sr = new StreamReader (st, Encoding.UTF8)) { string json = sr.ReadToEnd (); return json; } } }
では、上記メソッドの呼び出し側を書いていきます。
せっかくなので、Taskクラスを使って非同期に書いていきます。
protected void onbtnGetList (object sender, EventArgs e) { btnGetList.Sensitive = false; //別スレッドで実行するようにスケジューリングする var task = Task.Factory.StartNew (() => { String json = GetWheatherInfo (); WeatherInfo info = JsonConvert.DeserializeObject<WeatherInfo> (json); //タイトル string text = info.Title; String newLine = System.Environment.NewLine; text += newLine ; foreach (var item in info.Forecasts) { //いつ text += newLine + item.DateLabel; //お天気 text += newLine + item.Telop; //温度は取得できたら表示する if (item.Temperature != null) { if (item.Temperature.Max != null) { text += newLine + "最高気温:" + item.Temperature.Max.Celsius + "℃"; } if (item.Temperature.Min != null) { text += newLine + "最低気温:" + item.Temperature.Min.Celsius + "℃"; } } text += newLine; } text += newLine + info.Description.Text; return text; }); //取得完了後、UIスレッドで実行するようにスケジューリングする var taskScheduler = TaskScheduler.Current; task.ContinueWith (t => { tvJson.Buffer.Text = t.Result; btnGetList.Sensitive = true; }, taskScheduler); }
先頭に以下のusingを追加しておきます。
using Newtonsoft.Json; using System.Threading.Tasks;
ポイントとしては2点あります。
1点目は取得処理は別スレッドから呼んでいること。
ネットワークを経由する処理なので、ある程度時間がかかることも予想されます。
したがって、UIスレッドで呼ぶとフリーズしているように見えてしまいます。
コレを防ぐため、Taskで別スレッドから呼ぶようにしています。
2点目は、ContinueWithメソッドをUIスレッドで実行するようにしていること。
基本的に、ボタンやテキストビューを触るときはメインスレッドで行うようにしましょう。
Gtk#では、別スレッドで行ってもExceptionは発生しませんが、いつエラー扱いになるかわからないため、
お作法に従います。
TaskScheduler.Currentで現在実行しているタスクのスケジューラを取得しています。
本来ならばTaskScheduler. FromCurrentSynchronizationContextのほうが良いのですが、
なぜか取得できないので、代わりに呼び出してます。
これでJSONを取得し、パースし、データを表示することが出来ました。
取得するボタンを押し、こんな感じに画面が表示されていればOKです。