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

来源:互联网 发布:stm32编程实例 编辑:程序博客网 时间:2024/06/12 18:12

http://www.miiceic.org.cn/android/develop_6249.html

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 方法到服务操作之间的映射,还定义了用于调用操作和返回结果的消息格式。

  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <configuration>  
  3.   <system.web>  
  4.     <compilation debug="true" /></system.web>  
    •    <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>  
  5.   <system.serviceModel>  
  6.     <services>  
  7.       <service name="WCFRestHost.SmsService" behaviorConfiguration="default">  
  8.         <endpoint address="" binding="webHttpBinding" bindingConfiguration=""     contract="WCFRestHost.SmsService">  
  9.   <endpoint address="" binding="webHttpBinding" bindingConfiguration=""  
  10.           contract="WCFRestHost.SmsService">  
  11.         </endpoint>  
  12.         <host>  
  13.           <baseAddresses> 

(2) WCF 服务在 Winform Host 如何与服务端的UI进行数据交互

服务端代码,没什么特别的。一开始考虑用socket,但是socket自己维护双工通信太麻烦了,于是想到了WCF,在Winform Host下发布一个RESTful WCF服务,而作为客户端的Android实现Http通信也不是什么难事,服务端直接反序列化成服务端对象编码很便利。

WCF服务里公开一个static的BindingList作为短信容器(Tip:使用BindingList可以使得数据源变化立即反映到UI上,相比之下List可以说只是Oneway的容器),PC端可以通过UI往里添加要发送的短信,客户端则轮询取出要发送的短信进行发送;当客户端收到短信则通过WCF往容器里放入一条短信对象。 流程比较简单,大家一看就明白啦。

  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Linq;   
  4. using System.Runtime.Serialization;   
  5. using System.ServiceModel;   
  6. using System.Text;   
  7. using System.ServiceModel.Web;   
  8. using System.ComponentModel;   
  9. namespace WCFRestHost   
  10. {   
  11.     [ServiceContract]   
  12.     [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]   
  13. public class SmsService   
  14.     {   
  15.         static SmsService()   
  16.         {   
  17.             SmsList = new BindingList<SmsData>();   
  18.         }   
  19.   public static BindingList<SmsData> SmsList   
  20.         {   
  21.             get;   
  22.             private set;   
  23.         }   
  24.         public static event Action<SmsData> OnSmsRecieved;   
  25.         public static void AddData(SmsData data)   
  26.         {   
  27.  lock (SmsList)   
  28.             {   
  29.                 SmsList.Add(data);   
  30.             }   
  31.         }   
  32.         [OperationContract]   
  33.         [WebInvoke(UriTemplate="SetData", Method="POST", RequestFormat=WebMessageFormat.Json)]   
  34.  public void SetData(SmsData data)   
  35.         {   
  36.             var ctx = WebOperationContext.Current;   
  37.             data.SmsTime = DateTime.Now.ToString("MM-dd HH:mm:ss");   
  38.             //SmsList.Add(data);   
  39.             if (OnSmsRecieved != null)   
  40.     OnSmsRecieved(data);   
  41.             ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;   
  42.         }   
  43.         [OperationContract]   
  44.         [WebGet(UriTemplate = "GetData", ResponseFormat = WebMessageFormat.Json)]   
  45.         public SmsData GetData()   
  46.    {   
  47.             var ctx = WebOperationContext.Current;   
  48.             var data = SmsList.FirstOrDefault(sms => sms.State == 1);   
  49.             if (data != null)   
  50.             {   
  51.      data.State = 2;   
  52.                 data.SmsTime = DateTime.Now.ToString("MM-dd HH:mm:ss");   
  53.             }   
  54.             ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;   
  55.             return data;   
  56.         }   
  57.     }   
  58.   public class SmsData   
  59.     {   
  60.         public string Phone { getset; }   
  61.         public string Content { getset; }   
  62.         public string SmsTime { getset; }   
  63.   public string Name { getset; }   
  64.         // 0: recieved 1: presend 2: send   
  65.         public int State { getset; }   
  66.     }   
  67. }  

其次,ServiceHost 选择 WebServiceHost 类来承载利用 REST 编程模型的服务。即支持REST风格的Http Uri到服务方法的映射。另外,在 Winform 中 Host 为了让WCF 服务的通信不影响 UI 线程,需要为 WebServiceHost 单独创建线程。

  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Linq;   
  4. using System.Text;   
  5. using System.ServiceModel;   
  6. using System.Threading;   
  7. namespace WCFRestHost   
  8. {   
  9.     public class ThreadedServiceHost<T> : IDisposable where T : ServiceHostBase   
  10.     {   
  11.  const int SleepTime = 100;   
  12.         private ServiceHostBase _serviceHost = null;   
  13.         private Thread _thread;   
  14.         private bool _isRunning;   
  15.         public ThreadedServiceHost(Type serviceType)
  16.  {   
  17.             _serviceHost = (ServiceHostBase)Activator.CreateInstance(typeof(T), new object[] { serviceType });   
  18.             _thread = new Thread(ThreadMethod);   
  19.         }   
  20.         void ThreadMethod()   
  21.         {   
  22.             try  
  23.             {   
  24.  _isRunning = true;   
  25.                 _serviceHost.Open();   
  26.                 while (_isRunning)   
  27.                 {   
  28.                     Thread.Sleep(SleepTime);   
  29.                 }   
  30.                 _serviceHost.Close();
  31.    }   
  32.             catch (Exception)   
  33.             {   
  34.                 if (_serviceHost != null)   
  35.                 {   
  36.                     _serviceHost.Close();   
  37.                 }   
  38.             }   
  39.         }   
  40.  public void Open()   
  41.         {   
  42.             _thread.Start();   
  43.         }   
  44.         public void Stop()   
  45.         {   
  46.             lock (this)   
  47.             {   
  48.     _isRunning = false;   
  49.             }   
  50.         }   
  51.         public void Dispose()   
  52.         {   
  53.        Stop();   
  54.         }   
  55.     }   
  56. }  

服务端UI:

  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.ComponentModel;   
  4. using System.Data;   
  5. using System.Drawing;   
  6. using System.Linq;   
  7. using System.Text;   
  8. using System.Windows.Forms;   
  9. using System.ServiceModel.Web;   
  10. namespace WCFRestHost   
  11. {   
  12. public partial class frmMain : Form   
  13.     {   
  14.         public frmMain()   
  15.         {   
  16.             InitializeComponent();   
  17.         }   
  18. private ThreadedServiceHost<WebServiceHost> _threadHost;   
  19.         private void frmMain_Load(object sender, EventArgs e)   
  20.         {   
  21.             _threadHost = new ThreadedServiceHost<WebServiceHost>(typeof(SmsService));   
  22. _threadHost.Open();   
  23.             tssStatus.Text = "Listener is opening ...";   
  24.             SmsService.OnSmsRecieved += new Action<SmsData>(SmsService_OnSmsRecieved);   
  25.             DataBind();   
  26.         }   
  27.  void SmsService_OnSmsRecieved(SmsData obj)   
  28.         {   
  29.             Action<SmsData> addData = d => SmsService.AddData(d);   
  30.             dgvSms.Invoke(addData, obj);   
  31.         }   
  32.         private void DataBind()   
  33.         {   
  34. dgvSms.DataSource = SmsService.SmsList;   
  35.             dgvSms.ClearSelection();   
  36.             txtPhone.DataBindings.Add("Text", SmsService.SmsList, "Phone");   
  37.             txtTime.DataBindings.Add("Text", SmsService.SmsList, "SmsTime");   
  38. txtContent.DataBindings.Add("Text", SmsService.SmsList, "Content");   
  39.         }   
  40.         private void frmMain_FormClosing(object sender, FormClosingEventArgs e)   
  41.         {   
  42.             if (_threadHost != null)   
  43.                 _threadHost.Stop();   
  44.  }   
  45.         private void btnQuit_Click(object sender, EventArgs e)   
  46.         {   
  47.             this.Close();   
  48.         }   
  49.         private void btnSend_Click(object sender, EventArgs e)   
  50.  {   
  51.             var smsForm = new SendSms();   
  52.             smsForm.ShowDialog();   
  53.             //DataBind();   
  54.         }   
  55.  private void button1_Click(object sender, EventArgs e)   
  56.         {   
  57.             SmsService.SmsList.Add(new SmsData { Phone = "1" });   
  58.         }   
  59.     }   
  60. }  

注意:当收到短信时直接添加到BindingList中时会导致"跨线程更新UI的错误",因为WCF服务实际在子线程中,所以添加SmsData的操作必须使用Control.Invoke 进行调用,但是又不想把UI的引用直接交给WCF服务。。。呵呵,使用事件机制就可以很好的解决这一问题,让服务通知UI有新数据并让UI自己取添加数据。可以看到上面的服务代码中,公开了一个OnSmsRecieved事件。这样UI端监听这个事件,再使用 Control.Invoke 调用 AddData 方法。怎么样是不是很简单?不过每条短信是花1毛钱的T_T


 

 

  • 上一篇:Android Handler使用实例
  • 下一篇: 关于做android+J2ee系统集成开发的一点心得