(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