辛苦堆砌,转载请注明出处,谢谢!
最近项目需要使用SOAP进行WebService通信,采用了google提供的ksoap2-android进行开发,做一下记录。首先,使用Python创建了一个简单的基于Soap的服务,使用的是spyne模块,代码如下:
import logging
logging.basicConfig(level=logging.DEBUG)
from spyne import Application, rpc, ServiceBase, Unicode
from spyne import Iterable
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
class HelloWorldService(ServiceBase):
@rpc(Unicode, _returns=Unicode)
def say_hello(ctx, name):
return 'Hello, %s' % name
application = Application([HelloWorldService],
tns='http://spyne.examples.hello/',
in_protocol=Soap11(),
out_protocol=Soap11()
)
if __name__ == '__main__':
from wsgiref.simple_server import make_server
wsgi_app = WsgiApplication(application)
server = make_server('0.0.0.0', 8000, wsgi_app)
server.serve_forever()
我们创建了一个叫做HelloWorldService的服务,其中有一个方法是say_hello,需要一个name参数,命名空间为http://spyne.examples.hello/,在命令行执行python hello_world_service.py即可启动服务。服务的wsdl在这里无关紧要了,我们已经能够知道必要的信息了,所以不再贴出来。
下面看看我们的Android程序,首先下载ksoap2-android包,放置在项目app/libs目录下,然后将包添加到依赖,不再赘述。下面看看代码,我们基于ksoap2-android做了一个工具类
package com.yjp.ksoap2demo;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;
import java.util.Map;
public class SoapCommunicator {
private static final int TIMEOUT_MS = 10000;
public static final String ERROR_PREFIX = "error_";
public static final String ERROR_COMMON = ERROR_PREFIX + "common";
public static final String ERROR_TIMEOUT = ERROR_PREFIX + "timeout";
//用来判断是否call调用返回的是error
public static boolean isError(String str) {
return str.contains(ERROR_PREFIX);
}
public String call(String wsdlUrl, String namespace, String methodName, Map<String, Object> params) {
try {
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
SoapObject request = new SoapObject(namespace, methodName);
//设置参数
if (params != null && !params.isEmpty()) {
for (Map.Entry<String, Object> entry : params.entrySet()) {
PropertyInfo info = new PropertyInfo();
info.setNamespace(namespace);
info.setName(entry.getKey());
info.setValue(entry.getValue());
request.addProperty(info);
}
}
envelope.setOutputSoapObject(request);
HttpTransportSE ht = new HttpTransportSE(wsdlUrl, TIMEOUT_MS);
ht.call(null, envelope);
Object response = envelope.getResponse();
if (response != null) {
return response.toString();
} else {
return ERROR_COMMON;
}
} catch (Exception e) {
e.printStackTrace();
if (e instanceof java.net.SocketTimeoutException) {
return ERROR_TIMEOUT;
}
return ERROR_COMMON;
}
}
}
需要注意的是这里的call方法是同步调用,我们如果界面不想被卡,调用时应当采用一定的异步机制,稍后在MainActivity会看到。我们先看一下我们的布局
<?xml version="1.0" encoding="utf-8"?>
<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"
android:gravity="center"
tools:context="com.yjp.ksoap2demo.MainActivity">
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/send_request_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击发送请求"/>
</LinearLayout>
布局中有一个TextView用来显示响应的内容,点击按钮发起请求。MainActivity如下
package com.yjp.ksoap2demo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.HashMap;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
private TextView mContentTextView;
private SoapAsyncTask mSoapAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContentTextView = (TextView) findViewById(R.id.content);
Button sendRequestButton = (Button) findViewById(R.id.send_request_button);
sendRequestButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mSoapAsyncTask = new SoapAsyncTask();
mSoapAsyncTask.execute();
}
});
}
@Override
protected void onStop() {
super.onStop();
mSoapAsyncTask.cancel(false);
}
private class SoapAsyncTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... voids) {
SoapCommunicator communicator = new SoapCommunicator();
Map<String, Object> params = new HashMap<>();
params.put("name", "张三");
//这里使用的局域网,要保证IP可访问
String ret = communicator.call(
"http://192.168.6.27:8000/?wsdl",
"http://spyne.examples.hello/",
"say_hello",
params
);
return ret;
}
@Override
protected void onPostExecute(String s) {
if (SoapCommunicator.isError(s)) {
mContentTextView.setText("请求错误");
} else {
mContentTextView.setText(s);
}
}
}
}
我们使用了AsyncTask来实现异步机制,在doInBackground中调用了call方法,之后在MainActivity中使用了SoapAsyncTask,注意IP,配置为自己可访问的。
最后在manifest文件中添加权限,以使用网络
<uses-permission android:name="android.permission.INTERNET" />
如果可以访问服务,这样我们点击按钮时,就会显示Hello,张三