Xamarin.Android下獲取與解析JSON
一、新建專案
1.新建一個Android專案,並命名為為NetJsonList
2.右擊引用,選擇新增引用,引用System.Json.dll
二、同步請求
既然是跨平臺,我們自然不能按照java下的方式進行編寫,否則如何跨平臺呢,所以我們需要使用Syste.Net名稱空間下的兩個類:HttpWebRequest和HttpWebResponse。
首先開啟Resources/layout/Main.axml檔案
刪除其他上面的控制元件,並拖拽一個Text(large)到其中。
接著開啟MainActivity.cs檔案,並將如下程式碼寫入其中
1View Codenamespace NetJsonList 2 { 3 [Activity(Label = "NetJsonList", MainLauncher = true, Icon = "@drawable/icon")] 4 public class MainActivity : Activity 5 { 6 TextView tv; 7 8 protected override void OnCreate(Bundle bundle) 9 { 10 base.OnCreate(bundle);11 SetContentView(Resource.Layout.Main); 12 13 tv = FindViewById<TextView>(Resource.Id.textView1); 14 15 LoadXamarin(); 16 } 17 18 public void LoadXamarin() 19 { 20 //測試用 21 string url = "http://www.xamarin-cn.com/test.json"; 22 23 //建立一個請求 24 var httpReq = (HttpWebRequest)HttpWebRequest.Create(new Uri(url)); 25 26 //獲取響應 27 var httpRes = (HttpWebResponse)httpReq.GetResponse(); 28 29 //讀取響應的字串 30 String text = new StreamReader(httpRes.GetResponseStream()).ReadToEnd(); 31 tv.Text = text; 32 } 33 } 34 }
這裡我們通過HttpWebRequest的Create靜態方法建立了一個請求,那麼下面我們可以獲取響應了,筆者這裡直接使用StreamReader讀取了,以便顯示出來。
注:如果要在在短時間內高頻率的使用HTTP請改用HttpURLConnection。
最終顯示的結果如下所示:
但是我們在編碼的過程中絕對不能這麼做,因為這樣會使UI執行緒阻塞。意思就是使用者使用這個app時會出現卡頓,這樣對於任何一個人使用者來說都是很難忍受的,所以下面我們要採用非同步的方式來實現。
三、非同步請求
首先我們先把非同步請求回撥的方法寫出來:
1 //非同步回撥方法 2 public void ReadXamarin(IAsyncResult asyn) 3 { 4 var httpReq = (HttpWebRequest)asyn.AsyncState; 5 6 //獲取響應 7 using (var httpRes = (HttpWebResponse)httpReq.EndGetResponse(asyn)) 8 { 9 //判斷是否成功獲取響應 10 if (httpRes.StatusCode == HttpStatusCode.OK) 11 { 12 //讀取響應 13 var text = new StreamReader(httpRes.GetResponseStream()).ReadToEnd(); 14 15 //切換到UI執行緒,否則無法對控制元件進行操作 16 RunOnUiThread(() => 17 { 18 tv.Text = text; 19 }); 20 } 21 } 22 }View Code
通過AsyncState獲取HttpWebRequest的引用,然後再呼叫其EndGetResponse方法獲取響應,並將響應的字串顯示,特別需要注意的是我們這裡使用了RunOnUiThread方法,因為非同步回撥的方法不是使用UI執行緒執行的,這也是為什麼這樣做就不會阻塞UI執行緒了。
當然有了非同步操作的回撥方法,自然在呼叫的時候需要使用特殊的方法:
1 public void LoadXamarin() 2 { 3 //測試用 4 string url = "http://www.xamarin-cn.com/test.json"; 5 6 //建立一個請求 7 var httpReq = (HttpWebRequest)HttpWebRequest.Create(new Uri(url)); 8 httpReq.BeginGetResponse(new AsyncCallback(ReadXamarin), httpReq); 9 }View Code
所有的程式碼如下所示:
1 namespace NetJsonList 2 { 3 [Activity(Label = "NetJsonList", MainLauncher = true, Icon = "@drawable/icon")] 4 public class MainActivity : Activity 5 { 6 TextView tv; 7 8 protected override void OnCreate(Bundle bundle) 9 { 10 base.OnCreate(bundle); 11 SetContentView(Resource.Layout.Main); 12 13 tv = FindViewById<TextView>(Resource.Id.textView1); 14 15 LoadXamarin(); 16 } 17 18 public void LoadXamarin() 19 { 20 //測試用 21 string url = "http://www.xamarin-cn.com/test.json"; 22 23 //建立一個請求 24 var httpReq = (HttpWebRequest)HttpWebRequest.Create(new Uri(url)); 25 httpReq.BeginGetResponse(new AsyncCallback(ReadXamarin), httpReq); 26 } 27 28 //非同步回撥方法 29 public void ReadXamarin(IAsyncResult asyn) 30 { 31 var httpReq = (HttpWebRequest)asyn.AsyncState; 32 33 //獲取響應 34 using (var httpRes = (HttpWebResponse)httpReq.EndGetResponse(asyn)) 35 { 36 //判斷是否成功獲取響應 37 if (httpRes.StatusCode == HttpStatusCode.OK) 38 { 39 //讀取響應 40 var text = new StreamReader(httpRes.GetResponseStream()).ReadToEnd(); 41 42 //切換到UI執行緒,否則無法對控制元件進行操作 43 RunOnUiThread(() => 44 { 45 tv.Text = text; 46 }); 47 } 48 } 49 } 50 } 51 }View Code
四、利用await的非同步請求
通過c# 5.0 的新特性await我們可以寫更少的程式碼實現非同步,我們修改LoadXamarin方法:
1 public async void LoadXamarin() 2 { 3 //測試用 4 string url = "http://www.xamarin-cn.com/test.json"; 5 6 //建立一個請求 7 var httpReq = (HttpWebRequest)HttpWebRequest.Create(new Uri(url)); 8 var httpRes = (HttpWebResponse)await httpReq.GetResponseAsync(); 9 if (httpRes.StatusCode == HttpStatusCode.OK) 10 { 11 var text = new StreamReader(httpRes.GetResponseStream()).ReadToEnd(); 12 tv.Text = text; 13 } 14 }View Code
可以看到我們給這個方法加了一個async關鍵字,如果不加這個關鍵字,在該方法中是無法使用await的,下面我們就看到了這句程式碼:
1 var httpRes = (HttpWebResponse)await httpReq.GetResponseAsync();
當Ui執行緒執行到這句後,直接就會跳過去,不會等待。當然下面的方法也不會去執行了,只有當這個方法返回了結果之後,UI執行緒才會回到這句程式碼,並繼續執行下面的方法。當然這些只是障眼法,其實c#已經把下面的程式碼作為了非同步回撥執行的方法了:
1 if (httpRes.StatusCode == HttpStatusCode.OK) 2 { 3 var text = new StreamReader(httpRes.GetResponseStream()).ReadToEnd(); 4 tv.Text = text; 5 }
並且執行的執行緒依然是之前執行該方法的執行緒,如果你建立一個新的執行緒去執行。那麼就必須使用RunOnUiThread方法了。還有一個缺點就是不能在其中進行復雜的運算,否則還是會造成UI執行緒的阻塞。
五、解析JSON
在開始本節前,必須確保引用了正確的System.Json類庫,否則無法使用。
首先我們先看看json資料的格式:
1 { 2 "T": [ 3 "T1", 4 "T2", 5 "T3", 6 "T4" 7 ] 8 }
知道了json資料格式了,我們還要理解下System.Json中有哪些類:
JsonValue:是基礎類,代表一個Json值
JsonObject:繼承JsonValue,代表一個Json物件
JsonArray:繼承JsonValue,代表一個Json陣列
JsonPrimitive:繼承JsonValue,組織Json資料時用
JsonType:列舉,區分是什麼型別
而我們今天只需要使用到JsonObject和JsonArray,首先是JsonObject代表整個Json物件,我們可以通過它的靜態方法Load直接讀取流,比如下面的方式:
1 var text = JsonObject.Load(httpRes.GetResponseStream());
但是Load返回的是JsonValue,但是大多數情況都是代表一個Json物件,所以我們需要將它強制轉換成JsonObject型別,這樣我們就可以讀取其中的T了:
1 var text = (JsonObject)JsonObject.Load(httpRes.GetResponseStream()); 2 var array = (JsonArray)text["T"];
當然我們還需要使用Linq讀取這個陣列(注意要using System.Linq名稱空間)
1 var text = (JsonObject)JsonObject.Load(httpRes.GetResponseStream()); 2 var result = (from item in (JsonArray)text["T"] 3 select item.ToString()).ToArray();
這樣我們獲取了一個字串組了,result型別為string[]。為了顯示這個陣列,我們需要將當前的活動繼承自ListActivity:
然後將程式碼修改成如下所示:
1 namespace NetJsonList 2 { 3 [Activity(Label = "NetJsonList", MainLauncher = true, Icon = "@drawable/icon")] 4 public class MainActivity : ListActivity 5 { 6 protected override void OnCreate(Bundle bundle) 7 { 8 base.OnCreate(bundle); 9 LoadXamarin(); 10 } 11 12 public async void LoadXamarin() 13 { 14 //測試用 15 string url = "http://www.xamarin-cn.com/test.json"; 16 17 //建立一個請求 18 var httpReq = (HttpWebRequest)HttpWebRequest.Create(new Uri(url)); 19 var httpRes = (HttpWebResponse)await httpReq.GetResponseAsync(); 20 if (httpRes.StatusCode == HttpStatusCode.OK) 21 { 22 var text = (JsonObject)JsonObject.Load(httpRes.GetResponseStream()); 23 var result = (from item in (JsonArray)text["T"] 24 select item.ToString()).ToArray(); 25 ListAdapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, result); 26 } 27 } 28 } 29 }View Code
六、解決翻轉後重復執行
做到這步的可以在LoadXamarin中下一個斷點,當你點選模擬器並按下Ctrl+F12翻轉後你會發現LoadXamarin又重新執行了一次。這樣就帶來了一個問題,有時候我們僅僅只需要第一次獲取即可,該翻轉則會導致重複執行,那麼就會浪費使用者的流量同時也會浪費資源,所以我們這裡就需要能夠將這種狀態維持,這裡我們就需要重寫OnRetainNonConfigurationInstance事件,當然該方法需要返回一個Java.Lang.Object型別的返回值,而這個返回值就是我們需要儲存的狀態,當然這個狀態不會存在於OnCreate的bundle中,還是有一個專門的屬性LastNonConfigurationInstance,這樣我們可以將上面的程式碼改寫成如下:
1 namespace NetJsonList 2 { 3 [Activity(Label = "NetJsonList", MainLauncher = true, Icon = "@drawable/icon")] 4 public class MainActivity : ListActivity 5 { 6 class Test : Java.Lang.Object 7 { 8 public string[] Results { get; set; } 9 } 10 11 Test t; 12 13 protected override void OnCreate(Bundle bundle) 14 { 15 base.OnCreate(bundle); 16 LoadXamarin(); 17 } 18 19 //重寫該方法 20 public override Java.Lang.Object OnRetainNonConfigurationInstance() 21 { 22 return t; 23 } 24 25 public async void LoadXamarin() 26 { 27 t = LastNonConfigurationInstance as Test; 28 //判斷是否存在之前的狀態 29 if (t != null) 30 { 31 ListAdapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, t.Results); 32 } 33 else 34 { 35 36 //測試用 37 string url = "http://www.xamarin-cn.com/test.json"; 38 39 //建立一個請求 40 var httpReq = (HttpWebRequest)HttpWebRequest.Create(new Uri(url)); 41 var httpRes = (HttpWebResponse)await httpReq.GetResponseAsync(); 42 if (httpRes.StatusCode == HttpStatusCode.OK) 43 { 44 var text = (JsonObject)JsonObject.Load(httpRes.GetResponseStream()); 45 var result = (from item in (JsonArray)text["T"] 46 select item.ToString()).ToArray(); 47 t = new Test() 48 { 49 Results = result 50 }; 51 ListAdapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, result); 52 } 53 } 54 } 55 } 56 }
七、小結
通過上面的學習我們僅僅掌握了在跨平臺下最基礎的網路訪問以及Json資料解析的方法,還有在活動如何儲存當前的狀態。