大綱
本篇文章會先介紹為何Android中,用到網路請求時建議使用Volley框架,和Volley中如何使用GET請求網路資料。接下來會介紹在JAVA中,解析複雜的XML資料時,超級好用的SimpleXML以及如何使用。 最後再解說,我如何利用Volley框架結合SimpleXML解析網站上的RSS xml Data,本文會花多一點篇幅在這部分。
為何使用Volley框架請求網路數據
一談到Android的網路請求方式,相信大家第一個想到的就是HttpURLConnection和HttpClient,我以前也是這樣寫過來的。但若請求的東西單純還好,如果要POST、要傳複雜的參數或請求網路圖片等… 就會感到前所未有的煩躁,重複創建AsyncTask,一直重複copy/past的動作,還要擔心自己的寫法穩定性是否足夠。
幸運的是我亂逛網站時,偶然得知了Volley框架。Volley是Google在2013 Google I/O大會上發表的網路通訊框架,它將Http的通訊細節都經過封裝,讓使用者能輕鬆的請求網路數據。 GitHub中,比較複雜的APP也常看到Volley的蹤跡。但Volley有優點也有缺點,是否使用它要視情況而定。下面是Volley優缺點列表:
優點
- 高效能的GET/POST數據請求交互
- 幫忙處理複雜的網路圖片加載與緩存
- 性能穩定
- Google官方出的,保證功能全面
缺點
- 不適合大數據的上傳/下載
Volley設計的目標就是請求數據量不大但頻繁的網路資料,所以如果有大數據量的網路下載(ex 下載檔案文件),Volley表現就會不太好。
Volley的GET請求
因為這次我只會使用到Volley的GET,而Volley裡文字數據的請求有三個,StringRequest、JsonObjectRequest和JsonArrayRequest。JsonObjectRequest和JsonArrayRequest可以讓請求JSON數據時更快速,只可惜這次要請求的是XML數據,所以以下說明將使用StringRequest的GET方法做請求範例,JsonObjectRequest和JsonArrayRequest的用法其實和StringRequest相似,詳細會在以後用到時再做介紹,還有Volley最方便的網路圖片請求也會在以後做說明!
1 ) 引用volley包
感恩好心網友幫gradle和Maven建立Volley庫,讓我們能輕鬆使用框架。原repo請至這裡。
dependencies {
compile 'com.mcxiaoke.volley:library:1.0.18'
}
2 ) 宣告請求對列RequestQueue
RequestQueue用來緩存所有HTTP請求。RequestQueue內會按照一定算法發出網路請求。而RequestQueue本身就適合高開發,所以重複宣告多個很浪費資源,故RequestQueue物件大都設為全局Application的變數。
public class MyApp extends Application{
/*......*/
RequestQueue mQueue = Volley.newRequestQueue(context);
/*......*/
}
MyApp別忘記在AndroidManifest.xml加上android:name=”MyApp”,也別忘記加上網路權限
。
3 ) 創建StringRequest物件
/*
使用POST時用Request.Method.POST;使用GET時用Request.Method.GET
String url 為請求對像server的網址,因為是GET所以參數能直接夾帶在網址後
Response.Listener response successful時調用
Response.ErrorListener response error時調用
*/
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
//response成功時做的事
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error); //response失敗時做的事
}
});
4 ) 把建立好的request丟到RequestQueue執行,大功告成!
mQueue.add(stringRequest);
如果想知道更詳細的Volley使用方法,能參考這裡。
Java中,解析XML的方便套件:SimpleXML
台灣有很多活動網站,但不是沒提供完整的API,要不然就是API資料還死守XML不放……尋了好久,最後還是決定,用請求KKTIX的RSS xml資料,來獲得每日活動詳細。 RSS的xml 資料層次多,一層包一層很複雜,如果想快速取出想要的資訊,並跳過不需要的資料,好的XML解析套件就非常重要。 在這個JSON輕量級資料傳輸格式幾乎取代XML的時代,找出好的XML解析套件花了我不少時間,最後終於找到超直觀好用又穩定的套件:SimpleXML了!!
SimpleXML的使用思路是,先跟據要解析的XML文件創建一個java class(用SimpleXML規定的方式撰寫),再通過這個java class去取得XML文件內的具體值。 以下會做些簡易說明:
1 ) 以下為要解析的文件
<?xml version="1.0" encoding="UTF-8"?>
<list>
<entry id="1">
<name>John</name>
<gender>Boy</gender>
</entry>
</list>
2 ) 到官網下載SimpleXML Jar並加到Android Studio或Eclipse的library,官網的example也有教學能讓初學者更快上手。
3 ) 撰寫和要解析的XML文件相對應的java class
@Root(name="list", strict=true) //Root為根,如果<list>內不是每個元素都想去,strict要用false
public class Mylist {
@Attribute(required = true)
protected String id; // entry: id="1"
@Element(name = "name", required = true)
protected String Name;
@Element(name = "gender", required = true)
protected String Gender;
public String getID( ) {
return id;
}
public String getName( ) {
return Name;
}
public String getGender( ) {
return Gender;
}
}
怎麼樣!上面的java class對應看起來超直觀的吧!(灑花)
4 ) 通過這個java class去取得XML文件內的具體值
final Serializer serializer;
serializer = new Persister();
Mylist list = serializer.read(Mylist.class, String fileData); //得到Mylist
其中還有能將重複Item都整理成list的@ElementList,歡迎參考這裡。
利用Volley框架結合SimpleXML解析網站上的RSS xml Data
本次實作架構的UML如下:
- app資料夾:放置App全局資源
- AppController.java :繼承了Application,放置全局變量和method的地方
- AppRemoteConfig.java:放置資料請求目標端的URL
- bean資料夾
- Author.java、xmlBeanList.java、xmlEventBean.java:根據KKTIX RSS的xml data撰寫的映射物件。
- volleyResponse資料夾:放置Volley相關改寫
- SimpleXmlRequest.java:繼承自Volley的Request
,給Request做了改寫。
- SimpleXmlRequest.java:繼承自Volley的Request
思路始末
我原來的思路,是希望使用Volley的StringRequest把所有xml請求下來,到Response.Listener再用SimpleXML進行XML的解析。但是當我使用StringRequest請求完資料,並在Response.Listener用Log打印出來時,竟然只打印出一半的XML資料,後半資料都消失得莫名其妙,但StringRequest內確實是接收到全部的XML資料,這樣的結果讓我想到Response.Listener內可能不適合放置耗時邏輯,像是我本來將要做的,大量映射XML物件的邏輯…
經過翻找,很幸運地看到itsalif大大的SimpleXmlRequest.java貢獻,這位大大繼承了Volley的Request
AppController.java內的GetKKTIXRequestToEventBean(Context context)實作
public void GetKKTIXRequestToEventBean(Context context){
eventRequestQueue.cancelAll(VolleyTAG.KKTIX_ALL.getTAG()); //取消還在運行的同一個request
if(ifInternetOpen(context)) { //如果網路有開啟
/*
*AppRemoteConfig使用single pattern,確保不浪費空間資源
*xmlBeanList.class為給SimpleXML解析資料時的映射物件
*Response.Listener<xmlBeanList>為Volley資料回傳成功時調用,要實作onResponse method,xmlBeanList為回傳資料型態。
*Response.ErrorListener為錯誤時調用,要實作 onErrorResponse。
*/
SimpleXmlRequest<xmlBeanList> simpleRequest = new SimpleXmlRequest<xmlBeanList> (Request.Method.GET,
AppRemoteConfig.getInstance().getKKTIX_ALL_url(),
xmlBeanList.class,
new Response.Listener<xmlBeanList>() {
@Override
public void onResponse(xmlBeanList response) {
List<xmlEventBean> datas = response.getMatches();
for (xmlEventBean bean : datas) {
Log.i("success", "title: " + bean.getTitle() + " and " + bean.getAuthor().getName());
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.i("error", error.getMessage());
}
}
);
AppController.getInstance().addToRequestQueue(simpleRequest, VolleyTAG.KKTIX_ALL.getTAG()); //加入RequestQueue執行請求
}else{
//如果網路未開啟
Log.i("Internet Error","user phone didn't open internet.");
}
關於網路的判斷可以對照下面。
public boolean ifInternetOpen(Context context){
final ConnectivityManager connMgr = (ConnectivityManager)
this.getSystemService(Context.CONNECTIVITY_SERVICE); //呼叫App內建Service
final android.net.NetworkInfo wifi =
connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); //Wifi狀態
final android.net.NetworkInfo mobile =
connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); //3G、4G網路狀態
if( wifi.isAvailable() || mobile.isAvailable()){
return true;
}
else{
Toast.makeText(context, "請開起網路連線" , Toast.LENGTH_LONG).show();
return false;
}
}
之後在MainActivity就能很輕鬆的使用AppController.getInstance().GetKKTIXRequestToEventBean(MainActivity.this);載入網路上的KKTIX RSS的xml data。
撰寫解析KKTIX RSS的xml資料的映射物件
首先,觀察KKTIX RSS的xml data。網址如這,下面截圖已經取了片段說明。
由上面說明撰寫物件。
xmlBeanList.java
public class xmlBeanList {
@Root(name="feed",strict = false) //strict = false:非嚴格的feed tag中每個element和Attribute都要取,root為feed
@ElementList(entry = "entry", inline = true) //<entry></entry>的list
private List<xmlEventBean> beans; //集合,內容物對應到下面的xmlEventBean.java
public xmlBeanList() {
}
public List<xmlEventBean> getMatches() { //必須要有GET method才能拿到data
return beans;
}
}
xmlEventBean.java
public class xmlEventBean {
@Root(name="entry",strict = false) //root為entry
@Element(name = "title", required = true) //<title>...</title>,required設true:沒符合的話會丟error
private String title;
@Element(name="link",required = true) //<link rel="..." ..... href="..."/>
public class Link{
@Attribute(name="href",required = true)
private String url;
}
@Element(name = "summary", required = false) //<summary>...</summary>
private String summary;
@Element(name = "content", required = true) //<content>...</content>
private String timeANDplace;
@Element(name = "author", required = false)//<author>...</author>,對應到Author class
private Author author;
//必須要有GET method才能拿到data
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getTimeANDplace() {
return timeANDplace;
}
public void setTimeANDplace(String timeANDplace) {
this.timeANDplace = timeANDplace;
}
public Author getAuthor() {
return author;
}
}
Author.java
public class Author{
@Element(name="name",required = true)
private String name;
@Element(name="url",required = false)
private String url;
public String getName() {
return name;
}
public String getUrl() {
return url;
}
}
最後將 xmlBeanList.class給SimpleXmlRequest class當參數,讓SimpleXmlRequest內的parseNetworkResponse中,serializer.read(clazz, data,false)去將XML data都解析成物件。其中的false代表XML格式不嚴格,要加才不易抱錯。
限制選項Enum: VolleyTAG
最後,為了配合不同的請求目標URL,就要有相應不同的TAG管理,Enum就是個很好限制選項的寫法。使用自訂Enum並 限制住呼叫建構子的方法,讓使用者不會搞錯TAG,詳細教學見這。
public enum VolleyTAG {
//限制住本class只允許2種建構子: VolleyTAG("KKTIX_ALL") / VolleyTAG("GOV_EVENT")
KKTIX_ALL("KKTIX_ALL"),
GOV_EVENT("GOV_EVENT");
private String TAG=null;
VolleyTAG(String TAG) {
this.TAG=TAG;
}
public String getTAG(){
return TAG;
}
}
結果:成功映射出XML data物件
程式碼
git clone下面專案到自己的電腦後,使用 git checkout a5ab .。