Android 學習筆記之WebService實現遠程調用+內部原理分析...


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服務的無縫連接...

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM