Android 学习笔记之WebService实现远程调用+内部原理分析...

时间:2022-09-05 20:49:09

PS:终于可以抽出时间写写博客了,忙着学校的三周破实训外加替考...三周了,没怎么学习...哎...

学习内容:

1.WebService 实现远程方法的调用

  什么是WebService...

  WebService顾名思义,就是Web服务,WebService的数据传输格式是基于XML文档规范的,数据信息的传输就是以XML的形式来完成...由于XML不受平台和语言的限制,也正是由于这样的原因使得WebService可以实现远程调用,调用服务的语言可以是任意的.

  什么是SOAP协议...

  SOAP协议被称之为简单对象访问协议,它的作用是用来描述信息的传输格式...一条SOAP消息其实就是一个XML文档,SOAP可以规定一条消息是由谁进行发送的,并且由谁进行接收和处理,这就属于SOAP的封装,它基于XML的数据格式和Http的传输协议定义了一组标准的数据传输对象...说白了就是基于Http协议完成XML数据信息的传递...

  什么是WSDL...

  如果说SOAP用来完成数据信息的传递,那么WSDL就是规定数据信息以怎样的方式进行传递,WSDL是WebService的描述语言,目的是描述WebService上的每一个函数,我们知道函数调用的前提是:需要知道函数的功能,以及函数调用需要传递的相关参数,还有返回值,只有知道了这些点,我们才能够对一个函数进行调用,那么WSDL就是用来完成这个功能的,它基于XML文档,是一个机器可以解析的一个标准文档...其实就是一个描述应用函数的规范...

  什么是uddi...

  uddi是WebService的第三大要素,它的功能是完成WebService的注册和搜索,搜索其实就是在网络上的众多方法去查找我们需要的那个方法...注册则是生成一个新的函数...

  以上就是WebService的三大要素,可能有点蒙,总结来说就是,uddi完成调用服务的查找,查找到了调用函数时,在获取WSDL之后,我们就可以对其进行调用,调用的过程中,传递的对象为SOAP封装好的对象...

  如何调用WebService才是主要的....

  WebService的调用过程其实非常的简单...我们只需要知道调用函数时需要传递的参数,以及函数对应的url就可以轻松完成远程方法的调用...

调用WebService需要使用到一个ksoap2-android-assembly-3.0.0-jar-with-dependencies.jar的包...这个包的下载地址,我会在最后进行给出...

先附上一段代码...这个用来调用某城市天气情况的查询的一个远程方法...

package com.example.webservice_web;

import java.io.IOException;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;
import org.xmlpull.v1.XmlPullParserException;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast; public class MainActivity extends Activity implements View.OnClickListener { String NAMESPACE="http://WebXml.com.cn/"; //命名空间....
String url="http://www.webxml.com.cn/webservices/weatherwebservice.asmx"; //方法的url..
String METHOD_NAME="getWeatherbyCityName"; //需要调用的方法名...
String SOAP_ACTION="http://WebXml.com.cn/getWeatherbyCityName"; //一般为命名空间+方法名称... /**
* 上面这四种属性,我们可以通过方法的相关服务信息找到,相关信息其实就是WSDL文档..其中包含了上述信息的内容...
* 通过WSDL明确了方法调用时需要传递的相关参数,我们就可以调用这个远程方法了...
* */ String weatherToday;
SoapObject detail; //接收函数的返回值...
String weatherNow;
String weatherWillBe;
private TextView tv;
private EditText et;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et=(EditText) findViewById(R.id.et);
findViewById(R.id.bt).setOnClickListener(this);
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.bt: tv=(TextView) findViewById(R.id.tv);
SoapObject rpc=new SoapObject(NAMESPACE, METHOD_NAME);
String cityname=et.getText().toString();
rpc.addProperty("theCityName", cityname);//添加参数..目的为了调用服务端提供的方法...
/*
* 实例化SoapSerializationEnvelope对象...
* */
SoapSerializationEnvelope envelope=new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.bodyOut=rpc;
envelope.dotNet=true;
envelope.setOutputSoapObject(rpc);//设置输出的参数为rpc HttpTransportSE ht=new HttpTransportSE(url);
ht.debug=true;
try {
// Toast.makeText(getBaseContext(), "aa", Toast.LENGTH_LONG).show();
ht.call(SOAP_ACTION, envelope);
Toast.makeText(getBaseContext(), "bb", Toast.LENGTH_LONG).show();
detail=(SoapObject) envelope.getResponse();
String date=detail.getProperty(6).toString();
weatherToday="\n天气:"+date.split(" ")[1];
weatherToday=weatherToday+"\n气温:"+detail.getProperty(5).toString();
weatherToday=weatherToday+"\n风力:"+detail.getProperty(7).toString();
weatherNow=detail.getProperty(8).toString();
weatherWillBe=detail.getProperty(9).toString();
tv.setText(et.getText().toString()+weatherToday);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

  很简单的一段代码,但是有很多要说的地方...WebService的调用首先需要知道SOAP_ACTION,URL,Method,NAMESPACE...url我们可以直接获取的到...至于其他三个相关信息,我们可以通过查看WSDL文档直接就会获取到...那么知道了这些信息之后,我们才能够定义SOAP对象...SOAP把传递的参数,需要调用的方法,定义发送源和接收源...把这些信息封装在SOAP对象中,以数据流的形式发送到服务器,服务器在获取到这些数据之后,完成方法的调用,然后把返回的数据信息仍然以流的形式返回给客户端...这个客户端对返回的信息进行接收,那么整体就完成了远程方法的调用...下面说一下整体的实现过程...

  首先我们需要实例化一个SoapObject...实例化SoapObject对象需要调用new函数生成...下面是调用的源码过程...通过传递命名空间和方法名来实例化一个SoapObject对象..

 /**
* Creates a new <code>SoapObject</code> instance.
*
* @param namespace the namespace for the soap object
* @param name the name of the soap object
*/ public SoapObject(String namespace, String name) {
this.namespace = namespace;
this.name = name;
}

那么实例化一个对象之后我们需要向这个对象中加入属性值,调用addProperty()方法...其实目的就是为了传递参数...下面是源码的调用过程...

 /**
* Adds a property (parameter) to the object. This is essentially a sub element.
*
* @param name The name of the property
* @param value the value of the property
*/
//源码的意思就是为这个SoapObject对象配置两个属性,一个是名字属性,一个是值属性...
public SoapObject addProperty(String name, Object value) {
PropertyInfo propertyInfo = new PropertyInfo();
propertyInfo.name = name;
propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value.getClass();
propertyInfo.value = value;
return addProperty(propertyInfo);
}

  上面的源码我们可以看到,这里的属性添加是通过实例化PropertyInfo对象,然后再次调用addProperty()函数..(注:两次调用的函数名字一样,但是方法却是不一样的...)这里解释一下PorpertyInfo对象的作用...PorpertyInfo是一个类,这个类的作用其实就是对所有属性信息的一个保存...(这里属性信息大家可能并不清楚...其实就是对Object,Integer,String)等基本类型进行保存,保存的方式通过getClass()...获取每一个类的相关信息,用于赋值...其实不难理解,我们在调用addProperty()传递参数时,需要有参数的名字和值,那么自然也要有参数的属性,否则在获取SoapObject对象中的参数时,就无法知道传递的参数到底是什么属性...这样PropertyInfo的name,value,type就被附上了初值...最后通过调用addProperty方法..这里的addProperty(propertyInfo)传递的已经转化成了PorpertyInfo对象了...

/**
* Adds a property (parameter) to the object. This is essentially a sub element.
*
* @param propertyInfo designated retainer of desired property
*/
public SoapObject addProperty(PropertyInfo propertyInfo) {
properties.addElement(propertyInfo);
return this;
}

  这里调用properties.addElement()方法...addElement()方法...传递的参数是(E Object),这里的E,其实是Vector类型...Vector想必大家都清楚,是一个动态的对象数组,通过动态添加对象...这样Vector通过遍历的方式,将所有的属性对象都保存了起来...

/**
* Adds the specified object at the end of this vector.
*
* @param object
* the object to add to the vector.
*/
public synchronized void addElement(E object) {
if (elementCount == elementData.length) {
growByOne();
}
elementData[elementCount++] = object;
modCount++;
}

  那么保存了封装所有参数的SoapObject对象..自然需要进行传递给远程方法...完成调用....这里远程方法的调用需要实例化SoapSerializationEnvelope对象...被称之为序列化后的对象...序列化的含义其实就是把一个类或者对象转化成流的形式,这样方便进行传输和存储,序列化后的类或对象无需人为进行处理,如果没有进行序列化,我们需要人为的定义存储,转发的格式,还有转化成流都需要我们人为去书写..这样会相当的复杂...并且在反序列化同时,一般的运行环境都会自动进行类和对象的还原..如果人为进行还原的话...仍然会非常的复杂...这样通过序列化上面的SoapObject对象...就可以指定SoapObject对象传输的方式是以流的方式进行传输了...这里调用下面方法...

SoapSerializationEnvelope envelope=new SoapSerializationEnvelope(SoapEnvelope.VER11);下面是源码过程....这里实例化对象需要指定WebService的版本信息...

public SoapSerializationEnvelope(int version)
{
super(version);
addMapping(enc, ARRAY_MAPPING_NAME, PropertyInfo.VECTOR_CLASS);
DEFAULT_MARSHAL.register(this);
}

  这里调用了addMapping方法将保存了PropertyInfo的对象数组以键值对的形式保存在HashTable中...最后通过register方法将保存好的对象完成序列化封装的注册...完成了封装注册之后...还需要设置相关的属性...这里完成相关属性的设置...表示封装的对象为我们先前定义的SoapObject对象...设置访问的服务器为.Net服务器...设置最后输出的对象仍然为SoapObject对象...

 envelope.bodyOut=object;
envelope.dotNet=true;
envelope.setOutputSoapObject(object);

  封装好了所有的对象之后,就需要使用Http进行Url的连接了...通过HttptransportSE完成WSDL地址的连接...最后使用call方法完成该方法的调用...这样就完成了远程方法的调用,如果函数没有返回数据,那么我们就不需要进行获取了...只需要完成数据信息的上传就可以了..如果有返回数据,我们还需要定义一个SoapObject来完成返回数据的接收...通过返回的数据信息获取我们需要的相关信息并显示在自己的应用程序中,这样就完成了远程方法的调用...

 HttpTransportSE ht=new HttpTransportSE(url);
ht.debug=true;
try {
ht.call(SOAP_ACTION, envelope);
detail=(SoapObject) envelope.getResponse();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

  就如同刚开始的代码,最后返回的是一个SoapObject对象,我们接收到这个对象之后就可以获取一些我们想要的数据信息了...

(注:使用网络服务需要设置权限... <uses-permission android:name="android.permission.INTERNET"/>)....

这样就完成了远程方法的调用,实现了不同平台的应用程序之间完成通信...实现了Web服务的无缝连接...