根據W3C的定義,Web Services(Web服務)是一個用於支持網絡間不同機器互操作的軟件系統,它是一種自包含、自描述和模塊化的應用程序,它可以在網絡中被描述、發佈和調用,可以將它看作是基於網絡的、分佈式的模塊化組件。
Web Services是建立在通用協議的基礎之上,如HTTP、SOAP、UDDI、WSDL等,這些協議在操作系統、編程語言和對象模型的選擇上沒有任何傾向,因此有着很強的生命力。
Web Services的優勢在於提供了不同應用程序平臺之間的互操作,它使得基於組件的開發和Web相結合的效果達到最佳。它是基於HTTP協議的,調用請求和迴應消息都可以穿過防火牆,不需要更改防火牆的設置,這樣就避免了使用特殊端口進行通信時無法穿越防火牆的問題。
簡單的理解:通常我們所說的WebService都是遠程的某個服務器對外公開了某種服務,或者理解爲對外公開了某個功能或者方法,而我們可以通過編程來調用該服務以獲得我們需要的信息。例如:www.webxml.com.cn對外公開了手機號碼歸屬地查詢服務,我們只需要在調用該服務時傳入一個手機號段(號碼)就能立即獲取該號段的歸屬地信息。
更通俗的理解:通過使用WebService,我們能夠像調用本地方法一樣去調用遠程服務器上的方法。我們並不需要關心遠程的那個方法是Java寫的,還是PHP或C#寫的;我們並不需要關心遠程的方法是基於Unix平臺,還是Windows平臺,也就是說WebService與平臺和語言無關。
說到WebSerivce,就必須要知道SOAP和WSDL,它們到底和WebSerice有着怎麼的關係?上面已經提到,Web Services是建立在HTTP、SOAP、WSDL等通用協議的基礎之上。
SOAP(Simple Object Access Protocol,簡單對象訪問協議)是一種輕量級的、簡單的、基於XML的協議,被設計用於在分佈式環境中交換格式化和固化信息的簡單協議。也就是說,要進行通信,進行數據訪問傳輸,就必須依賴於一定的協議,而SOAP正是WebService通信中所依賴的一種協議。目前經常使用的SOAP協議有兩個版本:SOAP 1.1 和 SOAP 1.2。
WSDL(Web Services Description Language,即Web服務描述語言)是一種用來描述Web服務的XML語言,它描述了Web服務的功能、接口、參數、返回值等,便於用戶綁定和調用服務。它以一種和具體語言無關的方式定義了給定Web服務調用和應答的相關操作和消息。
在開發天氣預報的Android應用之前,首先需要找到一個可以對外提供天氣預報的Web Service,通過搜索發現站點http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx可以對外提供天氣預報的Web Service,因此程序會調用此站點的Web Service來實現天氣預報。(注意:如果該站點的天氣預報Web Service服務已經停止,那麼本程序將無法正常調用Web Service,那麼天氣預報的功能自然也就失效啦)
好啦,現在開始step by step地實現該應用程序。
第一步:獲取並使用KSOAP包
在Android SDK中並沒有提供調用WebService的庫,因此,需要使用第三方的SDK來調用WebService。PC版本的WebService庫非常豐富,但這些對Android來說過於龐大。適合手機的WebService客戶端的SDK有一些,比較常用的是KSOAP2。
KSOAP2 地址:http://code.google.com/p/ksoap2-android/
我下載的最新的是: ksoap2-android-assembly-2.5.2-jar-with-dependencies.jar
第二步:編寫調用Web Service的工具類 ServiceUtil.java
因爲本程序主要需要調用如下三個Web Service操作:
a.獲取省份:getRegionProvince方法
b.根據省份獲取城市:getSupportCityString方法
c.根據城市獲取天氣:getWeather方法
爲了讓應用界面更加美觀,可以訪問http://www.webxml.com.cn/images/weather.zip下載各種天氣圖標,可以使用這些天氣圖標來美化應用。
package com.example.xiaocool.webservicedemo.Util; import org.ksoap2.SoapEnvelope; import org.ksoap2.serialization.SoapObject; import org.ksoap2.serialization.SoapSerializationEnvelope; import org.ksoap2.transport.HttpTransportSE; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; /** * Created by JustYu on 2015/4/20. */ public class ServiceUtil { //定義 Web Service 的命名空間 static final String SERVICE_NS = "http://WebXml.com.cn/"; //定義Web service 提供服務的URL static final String SERVICE_URL = "http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx"; //調用 Web Service 獲取省份列表 public static List<String> getProvinceList() { //調用的方法 final String methodName = "getRegionProvince"; //創建HttpTransportSE 傳輸對象 final HttpTransportSE ht = new HttpTransportSE(SERVICE_URL); ht.debug = true; //使用SOAP1.1 協議創建Envelop對象 final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); //實例化 SoapObject對象 SoapObject soapObject = new SoapObject(SERVICE_NS, methodName); envelope.bodyOut = soapObject; //設置.Net 提供的web service 保持較好的兼容性 envelope.dotNet = true; FutureTask<List<String>> task = new FutureTask<List<String>>( new Callable<List<String>>() { @Override public List<String> call() throws Exception { //調用Web Service ht.call(SERVICE_NS + methodName, envelope); if (envelope.getResponse() != null) { //獲取服務器響應返回的SOAP消息 SoapObject result = (SoapObject) envelope.bodyIn; SoapObject detail = (SoapObject) result.getProperty(methodName + "Result"); //解析服務器響應的Soap return parseProvinceOrCity(detail); } return null; } } ); new Thread(task).start(); try { return task.get(); } catch (Exception e) { e.printStackTrace(); } return null; } //根據省份獲取城市列表 public static List<String> getCityListByProvince(String province) { //調用的方法 final String methodName = "getSupportCityString"; //創建HttpTransportSe final HttpTransportSE ht = new HttpTransportSE(SERVICE_URL); ht.debug = true; //實例化SoapObject 對象 SoapObject soapObject = new SoapObject(SERVICE_NS, methodName); //添加一個請求參數 soapObject.addProperty("theRegionCode", province); //使用SOAP1.1 協議創建Envelop 對象 final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.bodyOut = soapObject; //設置與。Net 提供的WEB Sertvice 提供較好的兼容性 envelope.dotNet = true; FutureTask<List<String>> task = new FutureTask<List<String>>( new Callable<List<String>>() { @Override public List<String> call() throws Exception { //調用Web service ht.call(SERVICE_NS + methodName, envelope); if (envelope.getResponse() != null) { //獲取服務器響應返回的SOAP消息 SoapObject result = (SoapObject) envelope.bodyIn; SoapObject detail = (SoapObject) result.getProperty(methodName + "Result"); //解析服務器響應的Soap 對象 return parseProvinceOrCity(detail); } return null; } } ); new Thread(task).start(); try { return task.get(); } catch (Exception e) { e.printStackTrace(); } return null; } private static List<String> parseProvinceOrCity(SoapObject detail) { ArrayList<String> result = new ArrayList<String>(); for (int i = 0; i < detail.getPropertyCount(); i++) { //解析出每個省份 result.add(detail.getProperty(i).toString().split(",")[0]); } return result; } public static SoapObject getWeatherByCity(String cityName) { final String methodName = "getWeather"; //創建HttpTransportSE 傳輸對象 final HttpTransportSE ht = new HttpTransportSE(SERVICE_URL); ht.debug = true; //使用Soap1.1協議創建 envelop對象 final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); SoapObject soapObject = new SoapObject(SERVICE_NS, methodName); //添加一個請求參數 soapObject.addProperty("theCityCode", cityName); envelope.bodyOut = soapObject; //設置與.net 提供的webservice 提供良好的兼容性 envelope.dotNet = true; FutureTask<SoapObject> task = new FutureTask<SoapObject>( new Callable<SoapObject>() { @Override public SoapObject call() throws Exception { //調用wenservice SERVICE_NS+methodName====SoapAction ht.call(SERVICE_NS + methodName, envelope); SoapObject result = (SoapObject) envelope.bodyIn; SoapObject detail = (SoapObject) result.getProperty(methodName + "Result"); return detail; } }); new Thread(task).start(); try { return task.get(); } catch (Exception e) { e.printStackTrace(); } return null; } }
第三步:設計應用的UI界面
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <!--省份列表--> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp" android:text="省份" /> <Spinner android:id="@+id/province" android:layout_width="fill_parent" android:layout_height="wrap_content"></Spinner> </LinearLayout> <!--城市列表--> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp" android:text="城市" /> <Spinner android:id="@+id/city" android:layout_width="fill_parent" android:layout_height="wrap_content"></Spinner> </LinearLayout> <!-- 顯示今天天氣的圖片和文本框--> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/todayWhIcon1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ImageView android:id="@+id/todayWhIcon2" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/weatherToday" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout> <!-- 顯示明天天氣的圖片和文本框--> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/tomorrowWhIcon1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ImageView android:id="@+id/tommorrowWhIcon2" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/tommorroToday" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" /> </LinearLayout> <!-- 顯示明天天氣的圖片和文本框--> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/afterdayWhIcon1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ImageView android:id="@+id/afterdayWhIcon2" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/afterdayToday" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" /> </LinearLayout> <TextView android:id="@+id/weatherCurrent" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
第四步:程序的主應用
package com.example.xiaocool.webservicedemo;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import com.example.xiaocool.webservicedemo.Util.ServiceUtil;
import org.ksoap2.serialization.SoapObject;
import java.util.List;
public class MainActivity extends ActionBarActivity {
private Spinner provinceSpinner;
private Spinner citySpinner;
private ImageView todayWhIcon1;
private ImageView todayWhIcon2;
private TextView TextWeatherToday;
private ImageView tommorrowWhIcon1;
private ImageView tommorrowWhIcon2;
private TextView TextWeathertommorrow;
private ImageView afterdayWhIcon1;
private ImageView afterdayWhIcon2;
private TextView TextWeatherafterday;
private TextView textWeatherCurrent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
provinceSpinner = (Spinner) this.findViewById(R.id.province);
citySpinner = (Spinner) this.findViewById(R.id.city);
todayWhIcon1 = (ImageView) this.findViewById(R.id.todayWhIcon1);
todayWhIcon2 = (ImageView) this.findViewById(R.id.todayWhIcon2);
TextWeatherToday = (TextView) this.findViewById(R.id.weatherToday);
tommorrowWhIcon1 = (ImageView) this.findViewById(R.id.tomorrowWhIcon1);
tommorrowWhIcon2 = (ImageView) this.findViewById(R.id.tommorrowWhIcon2);
TextWeathertommorrow = (TextView) this.findViewById(R.id.tommorroToday);
afterdayWhIcon1 = (ImageView) this.findViewById(R.id.afterdayWhIcon1);
afterdayWhIcon2 = (ImageView) this.findViewById(R.id.afterdayWhIcon2);
TextWeatherafterday = (TextView) this.findViewById(R.id.afterdayToday);
textWeatherCurrent = (TextView) this.findViewById(R.id.weatherCurrent);
//調用webservice 獲取省份
List<String> provinces = ServiceUtil.getProvinceList();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, provinces);
provinceSpinner.setAdapter(adapter);
//當省份的spinner 的選擇項被改變時
provinceSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
List<String> citys=ServiceUtil.getCityListByProvince(provinceSpinner.getSelectedItem().toString());
ArrayAdapter<String> cityadapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_spinner_item, citys);
citySpinner.setAdapter(cityadapter);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
//當城市的Spinner 的選擇項被改變是
citySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
ShowWeather(citySpinner.getSelectedItem().toString());
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
private void ShowWeather(String city){
String weatherToday=null;
String weatherTomorrow=null;
String weatherAfterday=null;
String weatherCurrent=null;
int iconToday[]=new int[2];
int iconTomorrow[]=new int[2];
int iconAfterday[]=new int[2];
//獲取遠程WEB service 返回的對象
SoapObject detail=ServiceUtil.getWeatherByCity(city);
//獲取天氣情況
// weatherCurrent=detail.getProperty(4).toString();
//解析今天的天氣
String date=detail.getProperty(7).toString();
weatherToday="今天:"+date.split(" ")[0];
weatherToday=weatherToday+"\n天氣:"+date.split(" ")[1];
weatherToday=weatherToday+"\n氣溫:"+detail.getProperty(8).toString();
weatherToday=weatherToday+"\n風力:"+detail.getProperty(9).toString()+"\n";
//更新當天的天氣情況
textWeatherCurrent.setText(weatherCurrent);
TextWeatherToday.setText(weatherToday);
}
}
第五步:添加訪問網絡的權限
注意:如果該站點的天氣預報Web Service服務已經停止,那麼本程序將無法正常調用Web Service,那麼天氣預報的功能自然也就失效啦