Android通过SOAP调用Webservice(.Net)接口

0x00 前言(废话)

项目中有遇到登录验证机制, 用户校验机制一般都是在服务器校验的, 本博客主要介绍Android的WebService调用方法, 登录验证机制以后有机会再另起文章.

效果图如下:

0x10 申请权限

AndroidManifest.xml中, 给APP添加网络权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:dist="http://schemas.android.com/apk/distribution"
          package="com.xxx.xxx">
    <!-- 主要是下面这一行 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    ...
</manifest>

0x20 载入SOAP的jar包

  1. 官网下载最新的soap包, 在该页面的下方:

  2. 下载好之后, 在Project视图把jar包添加到libs目录, 如下:
    直接copy到该目录即可
  3. 在Android Studio中添加依赖: File --> Project Structure... --> Modules --> app -->Dependencies:

    添加libs上的jar包到依赖

  4. 如果添加完成, 能在appbuild.gradledependencies看到:

dependencies {
    ...
    implementation files('libs/ksoap2-android-assembly-3.6.4-jar-with-dependencies.jar')
}

0x30 访问接口

1. WebService接口

推荐一个比较知名的测试接口: WeatherWS, 里面有6个方法供我们调用, 包括有输入参数/无输入参数:


在这里我拿第一个调用接口(方法)来举例, 点进getRegionCountry接口之后, 能看到各种SOAP版本的POSTGET请求:

POST /WebServices/WeatherWS.asmx HTTP/1.1
Host: ws.webxml.com.cn
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://WebXml.com.cn/getRegionCountry"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <getRegionCountry xmlns="http://WebXml.com.cn/" />
  </soap:Body>
</soap:Envelope>

2. 定义访问接口的类

① 定义常量

我们需要在Android Studio中的类中定义如下四个字符串常量:

private static String SOAP_ACTION="http://WebXml.com.cn/getRegionCountry";
private static String NAMESPACE="http://WebXml.com.cn/";
private static String METHOD_NAME="getRegionCountry";
private static String URL="http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?WSDL";
  • SOAP_ACTION: 顾名思义, 就是要访问的动作, 实际上就是NAMESPACE + METHOD_NAME.
  • NAMESPACE: 命名空间, 学过C++的同学应该比较熟悉(类似于using namespace std;), 就是一个方法所在的名字空间.
  • METHOD_NAME: 方法名, 即我们要调用的接口名(方法名).
  • URL: WebService的地址.

② 定义访问方法

private SoapObject getResultFromWebService() {
    // define webservice using namespace and it's method name
    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
    // if this methon need input parameter, we can use addProperty
    // request.addProperty("theCityCode", "深圳");

    // generate the SOAP-request-information of WebService method which we want to call
    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
//        envelope.bodyOut = request;
//        envelope.dotNet = true;
    envelope.setOutputSoapObject(request);
    try {
        // create HttpTransportSE object, it can define WebService WSDL Document by URL
        HttpTransportSE transportSE = new HttpTransportSE(URL);
        transportSE.call(SOAP_ACTION, envelope);
        SoapObject result = (SoapObject) envelope.bodyIn;
        SoapObject test = (SoapObject) result.getProperty(0);
        return test;
    } catch (Exception e) {
        Log.i(TAG, "Connect network wrong!");
        e.printStackTrace();
    }
    return null;
}

③ 多线程调用

我们需要开一个线程去掉用上面的方法, 不要用主线程, 才不会影响到界面状态:

这里想到有两种方式:

  • AsyncTask异步调用
  • 重写一个线程类, 继承自Thread.
    如果用第一种方法, 比较简单, 直接在doInBackground()方法中调用即可, 但是较难复用.
    如果用第二种方法, 则代码的封装性比较好, 耦合性低, 但是较为复杂. 这里我先用第一种方法示例.
class LoginToServerTask extends AsyncTask<Void, Integer, Boolean> {
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected void onPostExecute(Boolean aBoolean) {
        super.onPostExecute(aBoolean);

        try {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < result.getPropertyCount(); i++) {
                builder.append(result.getProperty(i));
            }
            Toast.makeText(getBaseContext(), builder.toString(), Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            Toast.makeText(getBaseContext(),getResources().
                    getText(R.string.str_no_response_check_network),Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }

    @Override
    protected Boolean doInBackground(Void... voids) {
        result = getResultFromWebService();
        return null;
    }
}

④ 在主线程开子线程

这里我们在onCreate()方法中调用上面的线程, 一般写在Button(或其他组件)的监听事件里:

mLoginBt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new LoginToServerTask().execute();
            }
        });

⑤ 补充: 带参数接口调用

首先, 我们需要去WSDL文档查找一下接口参数类型, 参数的数量, 我们找到的getSupportCityString需要输入一个参数, 如下:

POST /WebServices/WeatherWS.asmx HTTP/1.1
Host: ws.webxml.com.cn
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://WebXml.com.cn/getSupportCityString"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <getSupportCityString xmlns="http://WebXml.com.cn/">
      <!-- 下面的即为输入参数, theRegionCode表示参数的名称, string表示参数的类型-->
      <theRegionCode>string</theRegionCode>
    </getSupportCityString>
  </soap:Body>
</soap:Envelope>

带参数的需要给soapObject实例化的对象添加参数, 用addProperty, 方法如下:

request.addProperty("theRegionCode", "日本");
private static String SOAP_ACTION = "http://WebXml.com.cn/getSupportCityString";
private static String NAMESPACE = "http://WebXml.com.cn/";
private static String METHOD_NAME="getSupportCityString";
private static String URL="http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?WSDL";

private SoapObject getResultFromWebService(String volId, String passWd) {
    // define webservice using namespace and it's method name
    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
    // if this methon need input parameter, we can use addProperty
    request.addProperty("theRegionCode", "日本");

    // generate the SOAP-request-information of WebService method which we want to call
    // 设置SOAP协议的版本号, 根据服务端WebService的版本号设置
    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
    envelope.bodyOut = request;
    envelope.dotNet = true;
    envelope.setOutputSoapObject(request);
    try {
        // create HttpTransportSE object, it can define WebService WSDL Document by URL
        HttpTransportSE transportSE = new HttpTransportSE(URL);
        transportSE.call(SOAP_ACTION, envelope);
        SoapObject result = (SoapObject) envelope.bodyIn;
        SoapObject test = (SoapObject) result.getProperty(0);
        return test;
    } catch (Exception e) {
        Log.i(TAG, "Connect network wrong!");
        e.printStackTrace();
    }
    return null;
}

0x40 术语解释

WSDL

WSDL(Web服务描述语言,Web Services Description Language)是为描述Web服务发布的XML格式. 摘自维基百科

  • WSDL描述Web服务的公共接口。这个基于XML的接口描述了如何与Web服务通讯和使用的服务, 也就是描述与目录中列出的Web服务进行交互时需要绑定的协议和信息格式. 通常采用抽象语言描述该服务支持的操作和信息, 使用的时候再将实际的网络协议和信息格式绑定给该服务.
  • 我们用.Net开发的网站, 如果开放了接口, 就需要声明这些接口的输入参数, 输出等, WSDL就是描述了这些内容. 如: http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?WSDL

SOAP

SOAP(Simple Object Access Protocol的首字母缩写,即简单对象访问协议)是交换数据的一种协议规范, 在计算机网络Web服务(web service)中使用, 交换带结构的信息。摘自维基百科

  • 用XML格式来传递信息.
  • 举例: 一个SOAP消息可以发送到一个具有Web Service功能的Web站点,例如,一个含有房价信息的数据库,消息的参数中标明这是一个查询消息,此站点将返回一个XML格式的信息,其中包含了查询结果(价格,位置,特点,或者其他信息)。由于数据是用一种标准化的可分析的结构来传递的,所以可以直接被第三方站点所利用。
  • 协议规范由4部分组成:

SOAP封装(envelope),它定义了一个框架,描述消息中的内容是什么,是谁发送的,谁应当接受并处理它以及如何处理它们;
SOAP编码规则(encoding rules),它定义了一种序列化的机制,用于表示应用程序需要使用的数据类型的实例;
SOAP RPC表示(RPC representation),它定义了一个协定,用于表示远程过程调用和应答;
SOAP绑定(binding),它定义了SOAP使用哪种协议交换信息。使用HTTP/TCP/UDP协议都可以。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • WebService介绍 首先我们来谈一下为什么需要学习webService这样的一个技术吧.... 问题一 如果...
    Java3y阅读 9,552评论 5 139
  • 一、Java基础 1.写出下面代码的执行结果 2.写出下面代码的执行结果 3.写出下面代码的执行结果 (此题需写出...
    joshul阅读 509评论 0 1
  • 在一个方法内部定义的变量都存储在栈中,当这个函数运行结束后,其对应的栈就会被回收,此时,在其方法体中定义的变量将不...
    Y了个J阅读 4,412评论 1 14
  • 一、简历准备 1、个人技能 (1)自定义控件、UI设计、常用动画特效 自定义控件 ①为什么要自定义控件? Andr...
    lucas777阅读 5,186评论 2 54
  • 一、前言 数据库时按照数据结构来组织、存储和管理数据的仓库。数据库有多种类型,从简单的存储各种数据的表格到能够进行...
    小尛酒窝阅读 908评论 0 0