• Android访问WCF服务


    原文链接:http://www.cnblogs.com/VinC/archive/2011/02/24/1964049.html

    本章目的: 用Wcf建立可以上Android可以访问的数据服务, 数据传输格式采用比较适合于移动互联网传输的Json格式.

    服务的开发流程我们按照 服务契约(ServiceContract), 服务实现(Service), 实体对象模型(Model) 及服务发布的流程来介绍.

    由于自己对Http请求的链接认识的比较浅,对于有些问题没法做出清楚明了的解释, Android访问WCF这篇文章我会贴出来代码, 让后说明一下关注的地方, 不做深入研究.

    一. 服务契约(Contract)

    [ServiceContract]
        public interface IAccountJsonService
        {
            [OperationContract(Name = "GetAccountDataJson")]
            [WebGet(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "GetAccountData", BodyStyle = WebMessageBodyStyle.Bare)]
            List<Account> GetAccountData();
    
            [OperationContract(Name = "SendMessageJson")]
            [WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, UriTemplate = "SendMessage/{Message}", BodyStyle = WebMessageBodyStyle.Bare)]
            string SendMessage(string Message);
        }

    此契约定义了两个方法, GetAccountData(获取Account数据列表, 方法不带参数), SendMessage, 获取从客户端传过来的数据, 并返回;

    1. 这里面注意WebInvoke(SendMessage方法)这个Attribute, Method代表了Http的访问方法, 我们这是从服务器获取数据,是请求数据, 所以用GET, 这个也可以用另外一个Attribute来替代-WebGet(GetAccountData方法);

    2. 我们要给客户端返回Json数据,我们只需在WebInvoke or WebGet Attribute中指定ResponseFormat的格式即可, 这个从名字命名就可以看出来是制定返回的数据格式的.

    3. 要注意UriTemplate属性, 这个是指定我们请求时的方法路径, 后面给出示例.

    二. 服务实现(Service)

    public class AccountService : IAccountJsonService
    {
        public List<Account> GetAccountData()
        {
            return MockAccount.AccountList;
        }
        public string SendMessage(string Message)
        {
            return " Message:" + Message;
        }
    }

    此处只是实现了IAccountJsonService接口.

    三. 实体对象模型&模拟数据

    实体类定义:
    [DataContract]
        public class Account
        {
            [DataMember]
            public string Name { get; set; }
            [DataMember]
            public int Age { get; set; }
            [DataMember]
            public string Address { get; set; }
            [DataMember]
            public DateTime Birthday { get; set; }
        }
    模拟数据:
    public class MockAccount
       {
           public static List<Account> AccountList
           {
               get
               {
                   var list = new List<Account>();
                   list.Add(new Account { Name = "Bill Gates", Address = "YouYi East Road", Age = 56, Birthday = DateTime.Now });
                   list.Add(new Account { Name = "Steve Paul Jobs", Address = "YouYi West Road", Age = 57, Birthday = DateTime.Now });
                   list.Add(new Account { Name = "John D. Rockefeller", Address = "YouYi North Road", Age = 65, Birthday = DateTime.Now });
                   return list;
               }
           }
       }

    模拟数据返回一个Account的列表, 含有三条模拟数据, Birthday用DateTime.Now可是随时查看数据是不是最新生成的.

    四. 服务发布

    在这个例子里面, 我们的服务采用Console的发布形式, 如果采用IIS发布, 只要参考WCF的服务配置信息, 在IIS环境下配置就OK了.

    WCF配置信息

      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="">
              <serviceMetadata httpGetUrl="mex" httpGetEnabled="true"/>
              <serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true"/>
            </behavior>
          </serviceBehaviors>
          <endpointBehaviors>
            <behavior name="WebHttpBindingBehavior">
              <webHttp/>
            </behavior>
          </endpointBehaviors>
        </behaviors>
    
        <services>
          <service name="Hosting.AccountService">
            <endpoint address="xml" binding="webHttpBinding"  contract="Hosting.IAccountXmlService" behaviorConfiguration="WebHttpBindingBehavior"/>
            <!--<endpoint address="json" binding="webHttpBinding"  contract="Hosting.IAccountJsonService" behaviorConfiguration="WebHttpBindingBehavior"/>-->
            <host>
              <baseAddresses>
                <add baseAddress="http://127.0.0.1:82/AccountService"/>
              </baseAddresses>
            </host>
          </service>
        </services>
      </system.serviceModel>
    

    控制台进行服务的托管发布

    class Program
       {
           static void Main(string[] args)
           {
               using (ServiceHost host = new ServiceHost(typeof(AccountService)))
               {
                   host.Open();
                   Console.WriteLine("AccountService Address:");
                   foreach (var endpoint in host.Description.Endpoints)
                   {
                       Console.WriteLine(endpoint.Address.ToString());
                   }
                   Console.WriteLine("AccountService Started,Press any key to stop service...");
                   Console.ReadKey();
                   host.Close();
               }
           }
       }

    下篇将介绍Android如何访问我们编写的服务.

    示例代码下载

    此部分分为 建立Http请求 跟 接受WCF 返回的数据.

    一. 建立Http请求的方法

    protected String getRequest(String url, DefaultHttpClient client)
                throws Exception {
            String result = null;
            int statusCode = 0;
            HttpGet getMethod = new HttpGet(url);
            Log.d(TAG, "do the getRequest,url=" + url + "");
            try {
                getMethod.setHeader("User-Agent", USER_AGENT);
                // HttpParams params = new HttpParams();
    
                // 添加用户密码验证信息
                // client.getCredentialsProvider().setCredentials(
                // new AuthScope(null, -1),
                // new UsernamePasswordCredentials(mUsername, mPassword));
    
                HttpResponse httpResponse = client.execute(getMethod);
                // statusCode == 200 正常
                statusCode = httpResponse.getStatusLine().getStatusCode();
                Log.d(TAG, "statuscode = " + statusCode);
                // 处理返回的httpResponse信息
                result = retrieveInputStream(httpResponse.getEntity());
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
                throw new Exception(e);
            } finally {
                getMethod.abort();
            }
            return result;
        }

    参数URL: 我们要请求的地址

    Client:  这个可以直接用new DefaultHttpClient(new BasicHttpParams()) 来初始化.

    这个方法中需要注意RetrieveInputStream方法, 这个是当Http请求完成之后, 用来处理服务器返回数据的方法,

    二. 接受从WCF端传回的数据

    protected  String retrieveInputStream(HttpEntity httpEntity) {
            int  length = (int) httpEntity.getContentLength();
            if  (length < 0)
                length = 10000;
            StringBuffer stringBuffer = new  StringBuffer(length);
            try  {
                InputStreamReader inputStreamReader = new  InputStreamReader(
                        httpEntity.getContent(), HTTP.UTF_8);
                char  buffer[] = new char[length];
                int  count;
                while  ((count = inputStreamReader.read(buffer, 0, length - 1)) > 0) {
                    stringBuffer.append(buffer, 0, count);
                }
            } catch  (UnsupportedEncodingException e) {
                Log.e(TAG, e.getMessage());
            } catch  (IllegalStateException e) {
                Log.e(TAG, e.getMessage());
            } catch  (IOException e) {
                Log.e(TAG, e.getMessage());
            }
            return  stringBuffer.toString();
        }

    此方法在接受到WCF服务端返回的数据之后,  转换程String类型返回.

    附加内容:

    请求数据之前封装方法:

        private static final String BASE_URL = "http://10.0.2.2:82/BlogCategoryService/";
        private static final String EXTENSION = "Json/";;
        private static final String TAG = "API";
        private static final String USER_AGENT = "Mozilla/4.5";
    
        public JSONObject getObject(String sbj) throws JSONException, Exception {
            return new JSONObject(getRequest(BASE_URL + EXTENSION + sbj));
        }
    
        public JSONArray getArray(String sbj) throws JSONException,
                Exception {
            return new JSONArray(getRequest(BASE_URL + EXTENSION + sbj));
        }
    
        protected String getRequest(String url) throws Exception {
            return getRequest(url, new DefaultHttpClient(new BasicHttpParams()));
        }

    总结 : 此篇主要说明了Http请求的的两个阶段, 建立请求跟接受服务器返回的数据, 在下篇再主要说明如何处理服务端返回的JSON数据,并把数据显示在UI上面.

    1.写作背景:

      笔者想实现android调用webservice,可是网上全是不管对与错乱转载的文章,结果不但不能解决问题,只会让人心烦,所以笔者决定将自己整理好的能用的android调用webservice的实现分享给大家,供以后遇到相同需求的人能少走弯路。

      源码使用android studio编写,可以在github上面下载观看:https://github.com/jhscpang/TestWebSwervice。

    2.具体实现:

      本文的重点是android怎么调用webservice而不是用哪个webservice,所以这里就用网上传的比较多的计算来电归属地的webservice进行测试。这个webservice地址为:http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl。

    用浏览器访问这个网址可以看到如下界面:

    图中被圈起来的部分1说明soap版本为12, 被圈起来的部分2说明了namespace地址,这两个值稍后在代码中能用到。

    图中被圈起来的部分说明了调用的方法的名字,里面的说明文档告诉了输入参数和返回值等信息,这些信息稍后代码中也会用到。

      下面写请求webservice的方法,代码如下, 具体每句的解释有备注:

    复制代码
    /**
         * 手机号段归属地查询
         *
         * @param phoneSec 手机号段
         */
        public String getRemoteInfo(String phoneSec) throws Exception{
            String WSDL_URI = "http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL";//wsdl 的uri
            String namespace = "http://WebXml.com.cn/";//namespace
            String methodName = "getMobileCodeInfo";//要调用的方法名称
    
            SoapObject request = new SoapObject(namespace, methodName);
            // 设置需调用WebService接口需要传入的两个参数mobileCode、userId
            request.addProperty("mobileCode", phoneSec);
            request.addProperty("userId", "");
    
            //创建SoapSerializationEnvelope 对象,同时指定soap版本号(之前在wsdl中看到的)
            SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapSerializationEnvelope.VER12);
            envelope.bodyOut = request;//由于是发送请求,所以是设置bodyOut
            envelope.dotNet = true;//由于是.net开发的webservice,所以这里要设置为true
    
            HttpTransportSE httpTransportSE = new HttpTransportSE(WSDL_URI);
            httpTransportSE.call(null, envelope);//调用
    
            // 获取返回的数据
            SoapObject object = (SoapObject) envelope.bodyIn;
            // 获取返回的结果
            result = object.getProperty(0).toString();
            Log.d("debug",result);
            return result;
    
        }
    复制代码

      因为调用webservice属于联网操作,因此不能再UI线程中执行访问webservice,为了便于将结果反馈给UI线程,采用AsyncTask线程,代码如下:

    复制代码
     class QueryAddressTask extends AsyncTask<String, Integer, String> {
            @Override
            protected String doInBackground(String... params) {
                // 查询手机号码(段)信息*/
                try {
                    result = getRemoteInfo(params[0]);
    
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //将结果返回给onPostExecute方法
                return result;
            }
    
            @Override
            //此方法可以在主线程改变UI
            protected void onPostExecute(String result) {
                // 将WebService返回的结果显示在TextView中
                resultView.setText(result);
            }
        }
    复制代码

      然后在主线程中给用户设置使用该功能的方法,代码如下:

    复制代码
    private EditText phoneSecEditText;
        private TextView resultView;
        private Button queryButton;
        private String result;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            phoneSecEditText = (EditText) findViewById(R.id.phone_sec);
            resultView = (TextView) findViewById(R.id.result_text);
            queryButton = (Button) findViewById(R.id.query_btn);
    
            queryButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    // 手机号码(段)
                    String phoneSec = phoneSecEditText.getText().toString().trim();
                    // 简单判断用户输入的手机号码(段)是否合法
                    if ("".equals(phoneSec) || phoneSec.length() < 7) {
                        // 给出错误提示
                        phoneSecEditText.setError("您输入的手机号码(段)有误!");
                        phoneSecEditText.requestFocus();
                        // 将显示查询结果的TextView清空
                        resultView.setText("");
                        return;
                    }
    
                    //启动后台异步线程进行连接webService操作,并且根据返回结果在主线程中改变UI
                    QueryAddressTask queryAddressTask = new QueryAddressTask();
                    //启动后台任务
                    queryAddressTask.execute(phoneSec);
    
                }
            });
        }
    复制代码

      布局文件如下:

    复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:paddingTop="5dip"
        android:paddingLeft="5dip"
        android:paddingRight="5dip"
        >
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="手机号码(段):"
            />
        <EditText android:id="@+id/phone_sec"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:inputType="textPhonetic"
            android:singleLine="true"
            android:hint="例如:1398547"
            />
        <Button android:id="@+id/query_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:text="查询"
            />
        <TextView android:id="@+id/result_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal|center_vertical"
            />
    </LinearLayout>
    复制代码

      AndroidManifest文件如下:

    复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.jhsc.testwebservice" >
    
        <uses-permission android:name="android.permission.INTERNET" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme" >
            <activity android:name=".MainActivity" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    复制代码

      运行效果如下图:

     
    分类: Android
  • 相关阅读:
    mac下配置openCV
    K最短路 A*算法
    KMP算法
    北航复试机试题
    1385重建二叉树
    二维数组中的查找
    简单的单向链表
    Getting Started with WebRTC [note]
    我的c漏洞
    PeerConnection
  • 原文地址:https://www.cnblogs.com/Alex80/p/11111964.html
一二三 - 开发者的网上家园