WCF 实例 —— Android 短信助手 (WCF + Android)

 

 (1)最近开始学习 Android,为了更快上手于是给自己找个小课题来练习一下: WCF作为服务端开放RESTful Service,Android作为客户端将手机上的短信传给服务端显示,并轮询服务端是否有发送的短信取回并发送。(在电脑前就可以浏览即时的短信并能快速回复,呵呵还是挺有意思的)。先上几张图:

1. 客户端:(咦?怎么还用android1.5?没办法现在手机是1.5的,为了在真机上发布凑合用吧:))
 

2. 服务端:


其中涉及到的知识点如下:(恩文章标题有点跑偏了,其实大头在Android端。。。)

1. 服务端:
(1) RESTful WCF 如何作成 Winform Host
(2) WCF 服务在 Winform Host 如何与服务端的UI进行数据交互
2. 客户端:
(1) Android Timer 的应用
(2) Android Http 的交互
(3) Android Json数据的序列化/反序列化
(4) Android 短信拦截器的使用
(5) Android Sqlite 查询


--------------------------------------------------------------------------------

服务端
(1) RESTful WCF 如何作成 Winform Host

首先Binding 选择 webHttpBinding 使用 WebGetAttribute 或 WebInvokeAttribute 特性对各个服务操作进行批注。这定义了从 URI 和 HTTP 方法到服务操作之间的映射,还定义了用于调用操作和返回结果的消息格式。

view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
  <system.web> 
    <compilation debug="true" /> 
  </system.web> 
  <system.serviceModel> 
    <services> 
      <service name="WCFRestHost.SmsService" behaviorConfiguration="default"> 
        <endpoint address="" binding="webHttpBinding" bindingConfiguration="" 
          contract="WCFRestHost.SmsService"> 
        </endpoint> 
        <host> 
          <baseAddresses> 
            <add baseAddress="http://localhost:8732/RestWcfSMSSvc" /> 
          </baseAddresses> 
        </host> 
      </service> 
    </services> 
    <behaviors> 
      <serviceBehaviors> 
        <behavior name="default"> 
          <serviceMetadata httpGetEnabled="True"/> 
          <serviceDebug includeExceptionDetailInFaults="False" /> 
        </behavior> 
      </serviceBehaviors> 
    </behaviors> 
  </system.serviceModel> 
</configuration> 
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="WCFRestHost.SmsService" behaviorConfiguration="default">
        <endpoint address="" binding="webHttpBinding" bindingConfiguration=""
          contract="WCFRestHost.SmsService">
        </endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8732/RestWcfSMSSvc" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="default">
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>
(2) WCF 服务在 Winform Host 如何与服务端的UI进行数据交互

服务端代码,没什么特别的。一开始考虑用socket,但是socket自己维护双工通信太麻烦了,于是想到了WCF,在Winform Host下发布一个RESTful WCF服务,而作为客户端的Android实现Http通信也不是什么难事,服务端直接反序列化成服务端对象编码很便利。
WCF服务里公开一个static的BindingList<SmsData>作为短信容器(Tip:使用BindingList<T>可以使得数据源变化立即反映到UI上,相比之下List<T>可以说只是Oneway的容器),PC端可以通过UI往里添加要发送的短信,客户端则轮询取出要发送的短信进行发送;当客户端收到短信则通过WCF往容器里放入一条短信对象。流程比较简单,大家一看就明白啦。

+ expand sourceview plaincopy to clipboardprint?
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Runtime.Serialization;  
using System.ServiceModel;  
using System.Text;  
using System.ServiceModel.Web;  
using System.ComponentModel;  
namespace WCFRestHost  
{  
    [ServiceContract]  
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]  
    public class SmsService  
    {  
        static SmsService()  
        {  
            SmsList = new BindingList<SmsData>();  
        }  
        public static BindingList<SmsData> SmsList  
        {  
            get;  
            private set;  
        }  
        public static event Action<SmsData> OnSmsRecieved;  
        public static void AddData(SmsData data)  
        {  
            lock (SmsList)  
            {  
                SmsList.Add(data);  
            }  
        }  
        [OperationContract]  
        [WebInvoke(UriTemplate="SetData", Method="POST", RequestFormat=WebMessageFormat.Json)]  
        public void SetData(SmsData data)  
        {  
            var ctx = WebOperationContext.Current;  
            data.SmsTime = DateTime.Now.ToString("MM-dd HH:mm:ss");  
            //SmsList.Add(data);  
            if (OnSmsRecieved != null)  
                OnSmsRecieved(data);  
            ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;  
        }  
        [OperationContract]  
        [WebGet(UriTemplate = "GetData", ResponseFormat = WebMessageFormat.Json)]  
        public SmsData GetData()  
        {  
            var ctx = WebOperationContext.Current;  
            var data = SmsList.FirstOrDefault(sms => sms.State == 1);  
            if (data != null)  
            {  
                data.State = 2;  
                data.SmsTime = DateTime.Now.ToString("MM-dd HH:mm:ss");  
            }  
            ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;  
            return data;  
        }  
    }  
    public class SmsData  
    {  
        public string Phone { get; set; }  
        public string Content { get; set; }  
        public string SmsTime { get; set; }  
        public string Name { get; set; }  
        // 0: recieved 1: presend 2: send  
        public int State { get; set; }  
    }  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Web;
using System.ComponentModel;
namespace WCFRestHost
{
    [ServiceContract]
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
    public class SmsService
    {
        static SmsService()
        {
            SmsList = new BindingList<SmsData>();
        }
        public static BindingList<SmsData> SmsList
        {
            get;
            private set;
        }
        public static event Action<SmsData> OnSmsRecieved;
        public static void AddData(SmsData data)
        {
            lock (SmsList)
            {
                SmsList.Add(data);
            }
        }
        [OperationContract]
        [WebInvoke(UriTemplate="SetData", Method="POST", RequestFormat=WebMessageFormat.Json)]
        public void SetData(SmsData data)
        {
            var ctx = WebOperationContext.Current;
            data.SmsTime = DateTime.Now.ToString("MM-dd HH:mm:ss");
            //SmsList.Add(data);
            if (OnSmsRecieved != null)
                OnSmsRecieved(data);
            ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
        }
        [OperationContract]
        [WebGet(UriTemplate = "GetData", ResponseFormat = WebMessageFormat.Json)]
        public SmsData GetData()
        {
            var ctx = WebOperationContext.Current;
            var data = SmsList.FirstOrDefault(sms => sms.State == 1);
            if (data != null)
            {
                data.State = 2;
                data.SmsTime = DateTime.Now.ToString("MM-dd HH:mm:ss");
            }
            ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
            return data;
        }
    }
    public class SmsData
    {
        public string Phone { get; set; }
        public string Content { get; set; }
        public string SmsTime { get; set; }
        public string Name { get; set; }
        // 0: recieved 1: presend 2: send
        public int State { get; set; }
    }
}
 
其次,ServiceHost 选择 WebServiceHost 类来承载利用 REST 编程模型的服务。即支持REST风格的Http Uri到服务方法的映射。另外,在 Winform 中 Host 为了让WCF 服务的通信不影响 UI 线程,需要为 WebServiceHost 单独创建线程。

+ expand sourceview plaincopy to clipboardprint?
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.ServiceModel;  
using System.Threading;  
namespace WCFRestHost  
{  
    public class ThreadedServiceHost<T> : IDisposable where T : ServiceHostBase  
    {  
        const int SleepTime = 100;  
        private ServiceHostBase _serviceHost = null;  
        private Thread _thread;  
        private bool _isRunning;  
        public ThreadedServiceHost(Type serviceType)  
        {  
            _serviceHost = (ServiceHostBase)Activator.CreateInstance(typeof(T), new object[] { serviceType });  
            _thread = new Thread(ThreadMethod);  
        }  
        void ThreadMethod()  
        {  
            try 
            {  
                _isRunning = true;  
                _serviceHost.Open();  
                while (_isRunning)  
                {  
                    Thread.Sleep(SleepTime);  
                }  
                _serviceHost.Close();  
            }  
            catch (Exception)  
            {  
                if (_serviceHost != null)  
                {  
                    _serviceHost.Close();  
                }  
            }  
        }  
        public void Open()  
        {  
            _thread.Start();  
        }  
        public void Stop()  
        {  
            lock (this)  
            {  
                _isRunning = false;  
            }  
        }  
        public void Dispose()  
        {  
            Stop();  
        }  
    }  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Threading;
namespace WCFRestHost
{
    public class ThreadedServiceHost<T> : IDisposable where T : ServiceHostBase
    {
        const int SleepTime = 100;
        private ServiceHostBase _serviceHost = null;
        private Thread _thread;
        private bool _isRunning;
        public ThreadedServiceHost(Type serviceType)
        {
            _serviceHost = (ServiceHostBase)Activator.CreateInstance(typeof(T), new object[] { serviceType });
            _thread = new Thread(ThreadMethod);
        }
        void ThreadMethod()
        {
            try
            {
                _isRunning = true;
                _serviceHost.Open();
                while (_isRunning)
                {
                    Thread.Sleep(SleepTime);
                }
                _serviceHost.Close();
            }
            catch (Exception)
            {
                if (_serviceHost != null)
                {
                    _serviceHost.Close();
                }
            }
        }
        public void Open()
        {
            _thread.Start();
        }
        public void Stop()
        {
            lock (this)
            {
                _isRunning = false;
            }
        }
        public void Dispose()
        {
            Stop();
        }
    }
}
 

服务端UI:
+ expand sourceview plaincopy to clipboardprint?
using System;  
using System.Collections.Generic;  
using System.ComponentModel;  
using System.Data;  
using System.Drawing;  
using System.Linq;  
using System.Text;  
using System.Windows.Forms;  
using System.ServiceModel.Web;  
namespace WCFRestHost  
{  
    public partial class frmMain : Form  
    {  
        public frmMain()  
        {  
            InitializeComponent();  
        }  
        private ThreadedServiceHost<WebServiceHost> _threadHost;  
        private void frmMain_Load(object sender, EventArgs e)  
        {  
            _threadHost = new ThreadedServiceHost<WebServiceHost>(typeof(SmsService));  
            _threadHost.Open();  
            tssStatus.Text = "Listener is opening ...";  
            SmsService.OnSmsRecieved += new Action<SmsData>(SmsService_OnSmsRecieved);  
            DataBind();  
        }  
        void SmsService_OnSmsRecieved(SmsData obj)  
        {  
            Action<SmsData> addData = d => SmsService.AddData(d);  
            dgvSms.Invoke(addData, obj);  
        }  
        private void DataBind()  
        {  
            dgvSms.DataSource = SmsService.SmsList;  
            dgvSms.ClearSelection();  
            txtPhone.DataBindings.Add("Text", SmsService.SmsList, "Phone");  
            txtTime.DataBindings.Add("Text", SmsService.SmsList, "SmsTime");  
            txtContent.DataBindings.Add("Text", SmsService.SmsList, "Content");  
        }  
        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)  
        {  
            if (_threadHost != null)  
                _threadHost.Stop();  
        }  
        private void btnQuit_Click(object sender, EventArgs e)  
        {  
            this.Close();  
        }  
        private void btnSend_Click(object sender, EventArgs e)  
        {  
            var smsForm = new SendSms();  
            smsForm.ShowDialog();  
            //DataBind();  
        }  
        private void button1_Click(object sender, EventArgs e)  
        {  
            SmsService.SmsList.Add(new SmsData { Phone = "1" });  
        }  
    }  

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel.Web;
namespace WCFRestHost
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }
        private ThreadedServiceHost<WebServiceHost> _threadHost;
        private void frmMain_Load(object sender, EventArgs e)
        {
            _threadHost = new ThreadedServiceHost<WebServiceHost>(typeof(SmsService));
            _threadHost.Open();
            tssStatus.Text = "Listener is opening ...";
            SmsService.OnSmsRecieved += new Action<SmsData>(SmsService_OnSmsRecieved);
            DataBind();
        }
        void SmsService_OnSmsRecieved(SmsData obj)
        {
            Action<SmsData> addData = d => SmsService.AddData(d);
            dgvSms.Invoke(addData, obj);
        }
        private void DataBind()
        {
            dgvSms.DataSource = SmsService.SmsList;
            dgvSms.ClearSelection();
            txtPhone.DataBindings.Add("Text", SmsService.SmsList, "Phone");
            txtTime.DataBindings.Add("Text", SmsService.SmsList, "SmsTime");
            txtContent.DataBindings.Add("Text", SmsService.SmsList, "Content");
        }
        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_threadHost != null)
                _threadHost.Stop();
        }
        private void btnQuit_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        private void btnSend_Click(object sender, EventArgs e)
        {
            var smsForm = new SendSms();
            smsForm.ShowDialog();
            //DataBind();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            SmsService.SmsList.Add(new SmsData { Phone = "1" });
        }
    }
}
注意:当收到短信时直接添加到BindingList<SmsData>中时会导致"跨线程更新UI的错误",因为WCF服务实际在子线程中,所以添加SmsData的操作必须使用Control.Invoke 进行调用,但是又不想把UI的引用直接交给WCF服务。。。呵呵,使用事件机制就可以很好的解决这一问题,让服务通知UI有新数据并让UI自己取添加数据。可以看到上面的服务代码中,公开了一个OnSmsRecieved事件。这样UI端监听这个事件,再使用 Control.Invoke 调用 AddData 方法。怎么样是不是很简单?不过每条短信是花1毛钱的T_T


OK,接下来介绍下android端的代码。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/fangxinggood/archive/2011/05/23/6438762.aspx

posted @ 2012-01-31 23:22  黎明&岁月  阅读(1189)  评论(0编辑  收藏  举报