前幾日Android發布了4.0 Icecream,昨天上網發現Begining Book中有Edition 3的版本,比對一下,還是有相當的改動,不僅僅增加了tablet的部分,對原有的章節有有一些修訂,前後的調整等等。先按Edtion 2的順序看,相同章節的看Edtion 3,然後回頭看Edition 3的Chapter
24、25(E2的36)、26、27、28、29、44、45、46、47幾個新增章節。同時將模擬器改為Android 2.3的版本,已適應可能新增的改動。
訪問Internet是裝置的一個重要使用者,之前學習過如何嵌入Webkit,此外我們可以通過API來訪問Intenet。Android提供Apache HttpClient庫,基於此,其他協議,可以視為在HTTP封裝的格式,例如XML,JSON等。HTTP Client的詳細資料可在http://hc.apache.org/中擷取。
Android提供三個XML的解析器: 1、傳統的W3C DOM parser(org.w3c.dom); 2、SAX解析器(org.xml.sax); 3、之前Android學習筆記(三八):資源resource(上)中使用XmlPullParser。 另外提供JSON解析器(org.json)。當然也可以使用第三方的解析器。
實現的步驟如下:
- 建立HttpClient介面的對象,可以通過DefaultHttpClient來建立。
- 請求和HttpRequest相幫定,包括HttpGet,HttpPost,然手執行通過execute()來執行請求。
- 可以使用HttpResponsed對象處理返回,帶有response code(例如200,即200 OK),Http頭等,另外可以在execute()中攜帶ResponseHandler作為參數,將在此返回response body,然則此法可行但不推薦,因為通常來講我們應該response code。
下面是一個天氣widget的小例子,採用HttpClient,是以哦那個HttpGet發出對某個URL(google的天氣查詢網路頁面)請求,返回xml格式的天氣資訊,從中分析,並將結果通過WebKit Browser的方式程式,下面是尋找廣州天氣的返回。在例子中,第一學習HttpClient的用法,第二也學習XML的解析,此次採用W3C DOM解析器,通過NodeList和Element來進行分析。
<xml_api_reply version="1">
<weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0">
<forecast_information> <!-- 查詢地點資訊,只給出準備分析的部分 -->
… …
<postal_code data="廣州"/>
<forecast_date data="2011-10-27"/>
</forecast_information>
<current_conditions> <!-- 給出當前的天氣情況,只顯示準備分析的部分 -->
<condition data="局部多雲"/>
<temp_c data="27"/>
<humidity data="濕度: 48%"/>
<icon data="/ig/images/weather/partly_cloudy.gif"/>
<wind_condition data="風向: 北、風速:2 米/秒"/>
</current_conditions>
<forecast_conditions> <!-- 給出當天天氣預報 -->
<day_of_week data="周四"/>
<low data="21"/>
<high data="28"/>
<icon data="/ig/images/weather/mostly_sunny.gif"/>
<condition data="以晴為主"/>
</forecast_conditions>
<forecast_conditions> <!-- 給出明天天氣預報 -->
... ...
</forecast_conditions>
<forecast_conditions> <!-- 給出後天天氣預報 -->
… …
</forecast_conditions>
<forecast_conditions> <!-- 給出第三天天氣預報 -->
… …
</forecast_conditions>
</weather>
</xml_api_reply>
程式碼如下:
(這個介面屬於iGoogle的私人介面,已經被關閉,可以找其他的weather api,例如http://www.wunderground.com/weather/api/。 Wei , 2012.9 )
public class Chapter25Test1 extends Activity{
private WebView browser = null; //採用WebView來排版顯示的內容
private HttpClient client = null;
private List<Forecast> forecasts = new ArrayList<Forecast>();
//採用List來儲存未來幾天的天氣情況
private CurrentWeather currentCondition = null;
//儲存即時天氣情況和地點時間
private String format = "http://www.google.com/ig/api?hl=zh-cn&weather=%1s"; //Google XML天氣資訊的查詢API介面
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chapter_25_test1);
browser = (WebView) findViewById(R.id.c131_webkit);
//步驟1、建立HttpClient介面的對象,可以通過DefaultHttpClient來建立。
client = new DefaultHttpClient();
}
protected void onResume() {
super.onResume();
updateForecast("廣州"); //步驟2、向Internet請求廣州的天氣資訊,擷取資訊,並呈現
}
protected void onDestroy() {
super.onDestroy();
//步驟3、釋放HttpClient的資源,此步沒詳細跟蹤,按字面估計如一直串連無回複,則在Activity Destroy的時候,終止串連
client.getConnectionManager().shutdown();
}
//步驟2、向Internet請求廣州的天氣資訊,擷取資訊,並呈現
private void updateForecast(String citycode ){
String url = String.format(format, citycode);
//步驟2.1、產生HttpGet請求,並通過execute( )向Internet發出,在responseHandler中得到傳回值。
HttpGet getMethod = new HttpGet(url);
try{
ResponseHandler<String> responseHandle = new BasicResponseHandler();
String responseBody = client.execute(getMethod,responseHandle);
buildForecasts(responseBody); //步驟2.2 從返回中擷取天氣資訊
String page = generatePage(); //步驟2.3 通過HTML在webkit browser中呈現
browser.loadDataWithBaseURL(null, page, "text/html", "UTF-8", null);
}catch(Throwable t){
Toast.makeText(this,"Request failed : "+ t.toString(), 5000).show();
}
}
//步驟2.2 從返回中擷取天氣資訊,注意這種帶throws Exception的寫法
private void buildForecasts(String raw) throws Exception{
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(raw)));
//擷取即時天氣資訊和地點、時間資訊,方式和後面擷取未來天氣預報一樣,較為繁瑣,略過,資訊存放在currentCondition中
…. ….
//對於XML<node_name node_attribute=attribute_valule> node_vaule </node_name>
NodeList furCondition = doc.getElementsByTagName("forecast_conditions");
//有未來四日天氣預報,分別取出
for(int i = 0 ; i < furCondition.getLength(); i ++){
Element e = (Element)furCondition.item(i);
addWeather(e);
}
}
private void addWeather( Element e){
Forecast fc = new Forecast(getWeatherAttribute(e,"day_of_week"),
getWeatherAttribute(e,"low"),
getWeatherAttribute(e,"high"),
getWeatherAttribute(e,"icon"),
getWeatherAttribute(e,"condition"));
forecasts.add(fc);
}
//下面是從每組Element中讀取所需資訊,另外補充的是如果愛用Node,例如e.getFirstClide()擷取,也可以 node.getAttributes().getNamedItem("data").getNodeValue() 來獲得攜帶的屬性值
private String getWeatherAttribute(Element e, String name){
NodeList t = e.getElementsByTagName(name);
Element a = (Element)t.item(0);
return a.getAttribute("data");
}
//步驟2.3 通過HTML在webkit browser中呈現
private String generatePage(){
StringBuilder bufResult = new StringBuilder("<html><body><table width=\"100%\">");
bufResult.append("<h2><font color=\"red\">" + currentCondition.getCity() + "</font></h2>\n");
… … //顯示即時資訊
bufResult.append ("<table><tr><th width=\"25%\">Time</th>"+
"<th width=\"25%\">溫度</th><th colspan=\"2\" width=\"50%\">天氣情況</th></tr>");
for(Forecast forecast : forecasts){
bufResult.append("<tr><td align=\"center\">");
bufResult.append(forecast.getDate());
bufResult.append("</td><td align=\"center\">");
bufResult.append(forecast.getTemperature());
bufResult.append("</td><td align=\"right\">");
bufResult.append(forecast.getCondition());
bufResult.append("</td><td align=\"left\"><img src=\"http://www.google.com/");
bufResult.append(forecast.getIcon());
bufResult.append("\"></td></tr>");
}
bufResult.append("</table></body></html>");
return bufResult.toString();
}
private class Forecast{
…. /* 存放天氣預報資訊,並提供get擷取方法*/ …..
}
private class CurrentWeather{
…. /* 存放當前天氣、時間、地點,並提供get擷取方法*/ ….
}
}
通過經緯度擷取地點
在Google的天氣預報api中,也是可以輸入經緯度,格式為:http://www.google.com/ig/api?hl=zh-cn&weather=,,,23080000,113170000,這是廣州的經緯度。Android可以通過GPS擷取經緯度,然後所謂輸入參數,可以得到當前所在地的天氣。在模擬器中,可以通過預先配置經緯度來進行個類比。先開啟模擬器,然後在Eclipse的菜單Window -> Open Perspective
-> DDMS,進入配置,可在Control Panel中設定所需的經緯度。如右圖所示。
定位服務以後再學習。
需要注意
如果是要使用SSL協議,HttpClient並不支援。主要是因為需要決定如何處理SSL認證,是否接受所有認證,包括私人或者已經到期的正確,或者是否需要提示使用者。
簡單的HttpClient在預設下作為單線程使用。如果需要多線程,可以設定HttpClient支援多線程。
AndroidHttpClient
從Android2.2(API level 8)開始,可以使用AndroidHttpClient類,在android.net.http中,是HttpClient介面的一個實現,就如例子中的DefaultHttpClient。通過這個類,可以進行SSL管理;可通過靜態方法newInstance來直接填寫userAgent(report in your HTTP requests)並擷取AndroidHttpClient的執行個體,可以在HTTP頭能增加date資訊,支援gzip壓縮的內容。
和傳統的DefaultHttpClient不同,AndroidHttpClient不會自動儲存cookies,可通過HttpContext對象來處理。
此外,AndroidHttpClient不允許主線程使用,只能在後台線程運行。這點需要特別注意。
相關連結:我的Andriod開發相關文章