編輯:關於Android編程
使用Web Service進行網絡編程
Android應用通常都是運行在手機平台上,手機系統的硬件資源是有限的,不管是存儲能力還是計算能力都有限,在Android系統上開發、運行一些單用戶、小型應用是可能的,
但對於需要進行大量的數據處理、復雜計算的應用,還是只能部署在遠程服務器上,Android 應用將只是充當這些應用的客戶端。
為了讓Android應用與遠程服務器之間進行交互,可以借助子Java的RMI技術,但這要求遠程服務器程序必須采用Java實現;也可以借助於CORBA技術,但這種技術顯得過於復雜,除此之外,Web Service是一種不錯的選擇。
Web Service平台主要涉及的技術有SOAP(Simple Object Access Protocol,簡單對象訪問協議),WSDL( Web Service Description Language,Web Service 描述語言),UDDI(UniversalDescription, Description and Integration,統一描述、發現和整合協議)。
SOAP (SimpleObject Access Protocol,簡單對象訪問協議)是一種具有擴展性的;XML消息協議。SOAP允許一個應用程序向另一個應用程序發送XML消息,SOAP消息是從SOAP 發送者傳至SOAP接收者的單路消息,任何應用程序均可作為發送者或接收者。SOAP僅定義消息結構和消息處理的協議,與底層的傳輸協議獨立。因此,SOAP協議能通過HTTP, JMS 或SMTP協議傳輸。
SOAP依賴於XML文檔來構建,一條SOAP消息就是一份特定的XML文檔,SOAP消息包合如下三個主要元素:
?
必需的
?
可選的
? 必需的
就目前的SOAP消息的結構來看,
WSDL (WebService Description Language, Web Service 描述語言) 使用 XML 描述Web Service,包括訪問和使用WebService所必需的信息,定義該Web Service的位置、功能及如何通信等描述信息。
一般來說,只要調用者能夠獲取WebService對應的WSDL,就可以從中了解它所提供的服務及如何調用Web Service。因為一份WSDL文件淸晰地定義了三個方面的內容。
?
WHAT部分:用於定義Web Service所提供的操作(或方法),也就是Web Service
能做些什麼。由WSDL中的
? HOW部分:用於定義如何訪問Web Service,包括數據格式詳情和訪問Web Service操作的必要協議。也就是定義了如何訪問Web Service。
?
WHERE部分:用於定義Web Service位於何處,如何使用特定協議決定的網絡地址(如URL)指定。該部分使用
一份WSDL文檔通常可分為兩個部分:
?
第一個部分定義了服務接口,它在WSDL中由
?
WSDL的第二個部分定義了服務實現,它在WSDL中由
UDDI (UniversalDescription, Description and Integration,統一描述、發現和整合協議)是一套信息注冊規范,它具有如下特點:
? 基於Web。
? 分布式。
UDDI包括一組允許企業向外注冊WebService、以使其他企業發現訪問的實現標准。 UDDI的核心組件是UDDI注冊中心,它使用XML文件來描述企業及其提供的Web Service, 通過使用UDDI, Web Service提供者可以對外注冊Web Service,從而允許其他企業來調用該企業注冊的Web Service。Web Service提供者通過UDDI注冊中心的Web界面,將它所供的Web Service的信息加入UDDI注冊中心,該Web Service就可以被發現和調用。
Web Service使用者也通過UDDI注冊中心査找、發現自己所需的服務。當Web Service使用者找到自己所需的服務之後,可以將自己綁定到指定的Web Service提供者,再根據該 Web Service對應的WSDL文檔來調用對方的服務。
Java本身提供了豐富的WebService支持,比如Sun公司制定的JAX-WS 2規范,還有 Apache開源組織所提供的Axis1、Axis2、CXF等,這些技術不僅可以用於非常方便地對外提供Web Service,也可以用於簡化Web Service的客戶端編程。
對於手機等小型設備而言,它們的計算資源、存儲資源都十分有限,因此Android應用不大可能需要對外提供Web Service,Android應用通常只是充當Web Service的客戶端,調用遠程Web Serice。
Google為Android平台開發WebService客戶端提供了 ksoap2-android項目,但這個項目並未直接集成在Android平台中,還需要開發人員自行下載。
1) 登錄http://code.google.eom/p/ksoap2-android/站點,該站站點有介紹下載ksoap2-androi項目的方法。
2) 下載 ksoap2-android 項目的 ksoap2-android-assembly-3.0.0-RC4.jar-with-dependencies. jar 包。
3) 將下載的jar包放到android項目的libs目錄下即可。
為Android項目添加了ksoap2-android包之後,接下來借助 ksoap2-android項目來調用WebService所暴露出來的操作。
1) 創建HttpTransportSE對象,該對象用於調用WebService操作。
2) 創建 SoapSerializationEnvelope對象。
提示:從名稱來看SoapSerializationEnvelope代表一個SOAP消息封包;但ksoap2-android 項目對 SoapSerializationEnvelope 的處理比較特殊,它是HttpTransportSE調用WebService時信息的載體;客戶端需要傳入的參數,需要通過SoapSerializationEnvelope對象的bodyOut屬性傳給服務器;服務器響應生成的SOAP消息也通過該對象的body Out屬性來獲取。
3) 創建SoapObject對象,創建該對象時需要傳入所要調用WebService的命名空間、Web Service方法名。
4) 如果有參數需要傳給Web Service服務器端,調用SoapObject對象的addProperty(Stringname,Object value)方法來設置參數,該方法的name參數指定參數名;value參數指定參數值。
5) 調用SoapSerializationEnvelope的setOutputSoapObject()方法,或者直接對bodyOut屬性賦值,將前兩步創逆的SoapObject對象設為SoapSerializationEnvelope的傳出SOAP消息體。
6) 調用對象的call()方法,並以SoapSerializationEnvelope作為參數調用遠程WebService。
7) 調用完成後,訪問SoapSerializationEnvelope對象的bodyln屬性,該屬性返回一個 SoapObject對象,該對象就代表了Web Service的返回消息。解析該SoapObject對象,即可獲取調用Web Service的返回值。
在開發天氣預報的Android應用之前,首先需要找到一個可以對外提供天氣預報的Web Service,通過搜索,發現http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx 站點 可以對外提供天氣預報的WebService,因此程序將會調用該站點的Web Service來實現天氣預報。
為了讓應用界面更加美觀,可以訪問http://www.webxml.com.cn/images/weather.zip下載 各種天氣圖標,可以使用這些天氣圖標來美化應用。
本程序主要需要調用如下三個Web Seivice操作:
獲取省份。
根據省份獲取城市。
根據城市獲取天氣。
為了調用上面的三個WebService應用程序提供如下工具類。
public class WebServiceUtil
{
// 定義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
{
// 調用的方法
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> task = new FutureTask
>(
new Callable>()
{
@Override
public List
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
{
// 調用的方法
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 Service保持較好的兼容性
envelope.dotNet = true;
FutureTask> task = new FutureTask
>(
new Callable>()
{
@Override
public List
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
{
ArrayList
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";
final HttpTransportSE ht = new HttpTransportSE(SERVICE_URL);
ht.debug = true;
nbsp; final SoapSerializationEnvelope envelope =
new SoapSerializationEnvelope(SoapEnvelope.VER11);
SoapObject soapObject = new SoapObject(SERVICE_NS, methodName);
soapObject.addProperty("theCityCode", cityName);
envelope.bodyOut = soapObject;
// 設置與.Net提供的Web Service保持較好的兼容性
envelope.dotNet = true;
FutureTask
new Callable
{
@Override
public SoapObject call()
throws Exception
{
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;
}
}
上面的程序調用Web Service的方法還是沒有改變,前面兩個方法——獲取系統支持的省
份列表,根據省份獲取城市列表——將遠程Web Service返回的數據解析成List
上面的程序中調用WebService時將SoapSerializationEnvelope對象的dotNet屬性設為 true——因為上面這個網站是通過.NET來對外提供WebService的,因此需要將 SoapSerializationEnvelope 對象的 dotNet 屬性設為 true。
有了上面的調用WebService的工具類之後,接下來可以在Activity中使用該工具類來獲取天氣服務信息。該Activity使用了兩個Spinner讓用戶選擇省份、城市,當用戶選擇指定城市後,系統將會加載該程序的天氣信息。
該程序的界面布局代碼如下:
"1.0" encoding="utf-8"?>
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/province"/>
android:id="@+id/province"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/city"/>
android:id="@+id/city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:id="@+id/weatherCurrent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
android:layout_width="fill_parent"
android:layout_height="wrap_content">
android:id="@+id/todayWhIcon1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:id="@+id/todayWhIcon2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:id="@+id/weatherToday"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
android:layout_width="fill_parent"
android:layout_height="wrap_content">
android:id="@+id/tomorrowWhIcon1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:id="@+id/tomorrowWhIcon2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:id="@+id/weatherTomorrow"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
android:layout_width="fill_parent"
android:layout_height="wrap_content">
android:id="@+id/afterdayWhIcon1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:id="@+id/afterdayWhIcon2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:id="@+id/weatherAfterday"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
當程序加載時,程序會調用WebServiceUtil的getProvinceList()方法來獲取省份列表,並 使用第一個Spinner加載、顯示所有省份:當用戶改變選擇了省份之後,程序會調用
WebServiceUtil的getCityListByProvince(Stringprovince)方法來獲取該省份的全部城市:當用
戶改變選擇城市之後,程序會調用WebServiceUtil的getWeatherByCity(StringcityName)方法 獲取該城市的天氣。 該Activity的代碼如下: public
class GetWeather
extends Activity {
private Spinner
provinceSpinner;
private Spinner
citySpinner;
private ImageView
todayWhIcon1;
private ImageView
todayWhIcon2;
private TextView
textWeatherToday;
private ImageView
tomorrowWhIcon1;
private ImageView
tomorrowWhIcon2;
private TextView
textWeatherTomorrow;
private ImageView
afterdayWhIcon1;
private ImageView
afterdayWhIcon2;
private TextView
textWeatherAfterday;
private TextView
textWeatherCurrent;
@Override
public
void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
todayWhIcon1 = (ImageView) findViewById(R.id.todayWhIcon1);
todayWhIcon2 = (ImageView) findViewById(R.id.todayWhIcon2);
textWeatherToday = (TextView) findViewById(R.id.weatherToday);
tomorrowWhIcon1 = (ImageView) findViewById(R.id.tomorrowWhIcon1);
tomorrowWhIcon2 = (ImageView) findViewById(R.id.tomorrowWhIcon2);
textWeatherTomorrow = (TextView) findViewById(R.id.weatherTomorrow);
afterdayWhIcon1 = (ImageView) findViewById(R.id.afterdayWhIcon1);
afterdayWhIcon2 = (ImageView) findViewById(R.id.afterdayWhIcon2);
textWeatherAfterday = (TextView) findViewById(R.id.weatherAfterday);
textWeatherCurrent = (TextView) findViewById(R.id.weatherCurrent);
//
獲取程序界面中選擇省份、城市的Spinner組件
provinceSpinner = (Spinner) findViewById(R.id.province);
citySpinner = (Spinner) findViewById(R.id.city);
//
調用遠程Web Service獲取省份列表
List
ListAdapter adapter = new ListAdapter(this,
provinces);
//
使用Spinner顯示省份列表
provinceSpinner.setAdapter(adapter);
//
當省份Spinner的選擇項被改變時
provinceSpinner.setOnItemSelectedListener(new
OnItemSelectedListener()
{
@Override
public
void onItemSelected(AdapterView> source, View parent,
int position,
long id)
{
List
.getCityListByProvince(provinceSpinner.getSelectedItem()
.toString());
ListAdapter cityAdapter = new ListAdapter(GetWeather.this,
cities);
//
使用Spinner顯示城市列表
citySpinner.setAdapter(cityAdapter);
}
@Override
public
void onNothingSelected(AdapterView> arg0)
{
}
});
//
當城市Spinner的選擇項被改變時
citySpinner.setOnItemSelectedListener(new
OnItemSelectedListener()
{
@Override
public
void onItemSelected(AdapterView> source, View parent,
int position,
long id)
{
showWeather(citySpinner.getSelectedItem().toString());
}
@Override
public
void onNothingSelected(AdapterView> arg0)
{
}
});
}
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 = WebServiceUtil.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";
iconToday[0] = parseIcon(detail.getProperty(10).toString());
iconToday[1] = parseIcon(detail.getProperty(11).toString());
//
解析明天的天氣情況
date = detail.getProperty(12).toString();
weatherTomorrow = "明天:" + date.split("
")[0];
weatherTomorrow = weatherTomorrow + "\n天氣:" + date.split("
")[1];
weatherTomorrow = weatherTomorrow + "\n氣溫:"
+ detail.getProperty(13).toString();
weatherTomorrow = weatherTomorrow + "\n風力:"
+ detail.getProperty(14).toString() + "\n";
iconTomorrow[0] = parseIcon(detail.getProperty(15).toString());
iconTomorrow[1] = parseIcon(detail.getProperty(16).toString());
//
解析後天的天氣情況
date = detail.getProperty(17).toString();
weatherAfterday = "後天:" + date.split("
")[0];
weatherAfterday = weatherAfterday + "\n天氣:" + date.split("
")[1];
weatherAfterday = weatherAfterday + "\n氣溫:"
+ detail.getProperty(18).toString();
weatherAfterday = weatherAfterday + "\n風力:"
+ detail.getProperty(19).toString() + "\n";
iconAfterday[0] = parseIcon(detail.getProperty(20).toString());
iconAfterday[1] = parseIcon(detail.getProperty(21).toString());
//
更新當天的天氣實況
textWeatherCurrent.setText(weatherCurrent);
//
更新顯示今天天氣的圖標和文本框
textWeatherToday.setText(weatherToday);
todayWhIcon1.setImageResource(iconToday[0]);
todayWhIcon2.setImageResource(iconToday[1]);
//
更新顯示明天天氣的圖標和文本框
textWeatherTomorrow.setText(weatherTomorrow);
tomorrowWhIcon1.setImageResource(iconTomorrow[0]);
tomorrowWhIcon2.setImageResource(iconTomorrow[1]);
//
更新顯示後天天氣的圖標和文本框
textWeatherAfterday.setText(weatherAfterday);
afterdayWhIcon1.setImageResource(iconAfterday[0]);
afterdayWhIcon2.setImageResource(iconAfterday[1]);
}
//
工具方法,該方法負責把返回的天氣圖標字符串,轉換為程序的圖片資源ID。
private
int parseIcon(String strIcon)
{
if (strIcon ==
null)
return -1;
if ("0.gif".equals(strIcon))
return R.drawable.a_0;
if ("1.gif".equals(strIcon))
return R.drawable.a_1;
if ("2.gif".equals(strIcon))
return R.drawable.a_2;
if ("3.gif".equals(strIcon))
return R.drawable.a_3;
if ("4.gif".equals(strIcon))
return R.drawable.a_4;
if ("5.gif".equals(strIcon))
return R.drawable.a_5;
if ("6.gif".equals(strIcon))
return R.drawable.a_6;
if ("7.gif".equals(strIcon))
return R.drawable.a_7;
if ("8.gif".equals(strIcon))
return R.drawable.a_8;
if ("9.gif".equals(strIcon))
return R.drawable.a_9;
if ("10.gif".equals(strIcon))
return R.drawable.a_10;
if ("11.gif".equals(strIcon))
return R.drawable.a_11;
if ("12.gif".equals(strIcon))
return R.drawable.a_12;
if ("13.gif".equals(strIcon))
return R.drawable.a_13;
if ("14.gif".equals(strIcon))
return R.drawable.a_14;
if ("15.gif".equals(strIcon))
return R.drawable.a_15;
if ("16.gif".equals(strIcon))
return R.drawable.a_16;
if ("17.gif".equals(strIcon))
return R.drawable.a_17;
if ("18.gif".equals(strIcon))
return R.drawable.a_18;
if ("19.gif".equals(strIcon))
return R.drawable.a_19;
if ("20.gif".equals(strIcon))
return R.drawable.a_20;
if ("21.gif".equals(strIcon))
return R.drawable.a_21;
if ("22.gif".equals(strIcon))
return R.drawable.a_22;
if ("23.gif".equals(strIcon))
return R.drawable.a_23;
if ("24.gif".equals(strIcon))
return R.drawable.a_24;
if ("25.gif".equals(strIcon))
return R.drawable.a_25;
if ("26.gif".equals(strIcon))
return R.drawable.a_26;
if ("27.gif".equals(strIcon))
return R.drawable.a_27;
if ("28.gif".equals(strIcon))
return R.drawable.a_28;
if ("29.gif".equals(strIcon))
return R.drawable.a_29;
if ("30.gif".equals(strIcon))
return R.drawable.a_30;
if ("31.gif".equals(strIcon))
return R.drawable.a_31;
return 0;
} 上面的Activity代碼己經不再涉及調用WebService的代碼了,只是簡單地調用Web
Service操作,解析Web Service返回的SOAP消息包,並把SOAP消息包中的數據顯示出來。 未完待續..........
上一篇文章中我們講解了android中內存對象的序列化方式。由於android開發涉及到不同Activity的數據傳遞,對於基本數據類型數據的傳遞是沒有問題的,但是一旦涉
IT行業是一個踩在巨人肩膀上前進的行業,否則做的事情不一定有意義,所以我也是基於havlenapetr移植的ffmpeg基礎上做了些改進,他做的主要貢獻有:1. 移植了f
在AChat項目的開發過程中,項目要求無論終端是什麼時區設置、地處何方,終端的時間是否正確,post到服務器的數據包裡面的時間字段均要求跟服務器同步,也就是說,用戶買來一
在上篇文章給大家介紹了仿餓了嗎點餐界面兩個ListView聯動效果主要實現了2個ListView怎樣實現互相關聯,正好上篇博客review了ListView控件常規使用,