之前做过一次但是隔了一个月再做就有点忘了,果然好记性不如烂笔头!还是打算记一下。
现在网络上的一些有关这方面的文章都比较久远了虽然方法没错但是之前的网址都变了所以需要进行一些修改。
记录时间2016/10/10,apk完成时间2016/10/10
知识点概述
1. soap
soap一般跟web服务一起讲。通俗地讲,web服务就是将网络上的message消息作为输入然后再从网络上得到输出的过程。【需要注意的是,web服务并不仅仅指的是我们平常所用的网站,它包括一切可以由web访问到的服务。】
为了实现web服务的协同需要实现message交换和远程程序的调用,即基于XML进行的RPC(Remote Procedure Call),这个过程就是SOAP(Simple Object Access Protocol,简单对象访问协议)——网络对象之间进行情报交换的单纯的轻量级的协议。
soap协议的内容 详细请看这里 [翻译]Speaking SOAP with Android——谈一谈android的soap通信 这是我之前看了一个外国小哥写的有关于soap和ksoap2的文章的翻译,觉得对于初学者来说很有帮助,换句话说就是有助于入门。觉得看翻译太烦的,我还是在这里进行简单的总结:
- SOAP封装-Envelope:它定义了一个框架,描述消息中的内容是什么,是谁发送的,谁应当接受并处理它以及如何处理它们;
- SOAP编码规则-Encoding Rules:它定义了一种序列化机制,用于表示应用程序需要使用的数据类型的实例;
- SOAP RPC表示-RPC Representation:它定了一个协定,用于表示远程过程调用和应答;
SOAP绑定-Binding:它定义了SOAP使用哪种协议交换信息。使用HTTP/TCP/UDP协议都可以
SOAP消息基本上是从发送端到接收端的单向传输,但它们常常结合起来执行类似于请求 / 应答的模式。所有的 SOAP消息都使用 XML 编码。一条 SOAP消息就是一个包含有一个必需的 SOAP 的封装包,一个可选的 SOAP 标头head和一个必需的 SOAP 主体body的 XML 文档。
2.android中的soap协议
目前的开发平台并不支持内嵌的soap协议的开发,在Android平台调用Web Service需要依赖于第三方类库ksoap2,它是一个SOAP Web service客户端开发包,主要用于资源受限制的Java环境如Applets或J2ME应用程序(CLDC/ CDC/MIDP)。所以如果你想要开发有soap通信的Android程序需要外部导入soap协议的包。
3.wsdl
WSDL:Web Services Description Language的缩写,是一个用来描述Web服务和说明如何与Web服务通信的XML语言。为用户提供详细的接口说明书。
ksoap-2-android使用
1.在使用之前你需要下载ksoap-a-android 2.4.5.jar包,下载地址:http://www.webservicex.net/CurrencyConvertor.asmxwebservice
2.需要一个wsdl文档来进行测验,我们将手机号码归属地查询的地址作为测试例子http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx 只是用里面其中一个方法getMobileCodeInfo
下载并将外部jar包加入工程:具体过程查看-> Android Studio 导入第三方库——*.jar包
在使用ksoap2之前我们需要会读懂wsdl文档,意思就是能够从wsdl文档中找到soapAction、namespaceAddress等信息。我们打开手机号码归属地查询的wsdl文档 http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL
函数名 getMobileCodeInfo
参数列表
mobileCode = 字符串(手机号码,最少前7位数字)
userID = 字符串(商业用户ID) 免费用户为空字符串
返回值
字符串(手机号码:省份 城市 手机卡类型)
1.首先实例化一个SoapObject对象
SoapObject soapObject=new SoapObject(namespaceAddress,methodName);
参数:
namespaceAddress 在wsdl文档里阿敏可以找到targetnamesapce
methodName 就是函数名字
2.添加函数的参数
soapObject.addProperty("mobileCode",num);
soapObject.addProperty("userID", "");
参数名字需要跟wsdl文档中的名称和顺序一致
3.封装
//生成调用WebService方法的SOAP请求信息,并指定SOAP的版本
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.bodyOut = soapObject;
// 设置是否调用的是dotNet开发的WebService
envelope.dotNet = true;
// 等价于envelope.bodyOut = soapObject;
envelope.setOutputSoapObject(soapObject);
SoapEnvelope.VER11表示协议版本,可以试一下11,12和10
4.调用webservice
urlAddress或者叫做Endpoint是包含asmx的地址
HttpTransportSE httpTransportSE = new HttpTransportSE(urlAddress);
try {
//call函数参数soapAction在wsdl文档中可以看到
//这个操作会产生异常需要对异常进行处理
httpTransportSE.call(soapAction,envelope);
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
5.获得返回信息并取得结果
SoapObject object = (SoapObject) envelope.bodyIn;
String info=object.getProperty(0).toString();
取得的是一个xml结果,但是在第一句话转换成SoapObject对象直接然后用getProperty函数直接取得结果,转成字符串。
下面是wsdl文档中关于函数getMobileCodeInfo的参数格式和返回格式
<s:schema elementFormDefault="qualified" targetNamespace="http://WebXml.com.cn/">
<s:element name="getMobileCodeInfo">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="mobileCode" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="userID" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
<s:element name="getMobileCodeInfoResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="getMobileCodeInfoResult" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
下面是soap1.1格式下的请求和响应
实例
1.界面
输入框:EditText id=edtNum
按 钮:Button id=btnok
显示框:TextView id=tvInfo
输入框背景:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid
android:color="#ffffff"/>
<padding
android:bottom="10dp"
android:left="10dp"
android:right="10dp"
android:top="10dp"/>
<stroke
android:dashWidth="0dp"
android:width="1dp"
android:color="#000000"/>
<corners
android:radius="10dp"/>
</shape>
2.实现
public class MainActivity extends AppCompatActivity {
private static final String namespaceAddress="http://WebXml.com.cn/";
private static final String urlAddress="http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx";
private static final String methodName="getMobileCodeInfo";
private static final String soapAction="http://WebXml.com.cn/getMobileCodeInfo";
EditText edtNum;
Button btnok;
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//设置在主线程里直接运行子线程,下面主线程内不能用new thread
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork()
.penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().detectLeakedClosableObjects()
.penaltyLog().penaltyDeath().build());
/**
* edtNum号码输入框
* tv 归属地查询结果显式TextView
* btnok 点击查询按钮
*/
edtNum=(EditText)findViewById(R.id.edtNum);
tv=(TextView)findViewById(R.id.tvInfo);
btnok=(Button)findViewById(R.id.btnok);
btnok.setOnClickListener(myListener);
}
/**
* getTelInfo函数用于查询手机号码归属地并显示在文本框内
* @param num String 类型的手机号码,长度不能少于7位数
*/
public void getTelInfo(String num)
{
SoapObject soapObject=new SoapObject(namespaceAddress,methodName);
//soapObject添加参数的顺序必须与asmx里面的参数顺序一致,名称保持一致
soapObject.addProperty("mobileCode",num);
soapObject.addProperty("userID", "");
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);
envelope.bodyOut = soapObject;
envelope.dotNet = true;
envelope.setOutputSoapObject(soapObject);
HttpTransportSE httpTransportSE = new HttpTransportSE(urlAddress);
try {
httpTransportSE.call(soapAction,envelope);
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
SoapObject object = (SoapObject) envelope.bodyIn;
String info=object.getProperty(0).toString();
tv.setText(info);
}
Button.OnClickListener myListener=new Button.OnClickListener()
{
@Override
public void onClick(View v) {
final String number=edtNum.getText().toString().trim();
if(number.equals("")){
Toast.makeText(getApplicationContext(),"号码不能为空!",Toast.LENGTH_SHORT)
.show();
}else if(number.length()<7||number.length()>11) {
Toast.makeText(getApplicationContext(),"号码格式不对,无法获取信息!",Toast.LENGTH_SHORT)
.show();
}
else {
getTelInfo(number);
}
}
};
}
注意1:打开应用的网络访问权限——修改Manifest.xml
在application外层上面或者下面写上下面这句权限:
<uses-permission android:name="android.permission.INTERNET"/>
注意2:主线程中不能直接访问网络的线程可以用新建线程的进行访问。
最暴力的方法就是在主线程中加入:
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork()
.penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().detectLeakedClosableObjects()
.penaltyLog().penaltyDeath().build());