最近兴趣使然尝试了一下使用DotNet技术实现新浪微博PC客户端,几天时间,目前实现登录、微博列表、发布纯文本微博功能,新浪API调用基本没什么难度,在微博列表形式处理上着实让我烦躁了一阵子,Windows Form使用不多,这次开发也感觉有些捉襟见肘。

环境:
操作系统:Windows 7 Ultimate
集成开发环境:Visual Studio 2010 Ultimate
.NET Framework:3.5

先看一下截图:
1、登录界面

2、 登录

3、第一次运行主界面加载

4、主页面

5、翻页

6)如果博文中有图,点击小图可以查看大图

新浪微博API返回的数据类型有两种:XML和JSON。在登录时,我直接用DataSet读的XML数据存储到一个静态用户信息类中;而博客列表数据我读取的是JSON格式的数据,并将JSON格式的数据反序列化为泛型对象(类)集合,这里的类是嵌套类。

然后绘制列表,显示数据的控件全部是动态添加的,这里最烦的是位置,可能也是这个原因导致每页20条数据加载比较慢(接口默认一页返回20条),最快也要2秒,经常要4-6秒,后面提供下载,大家可以试试。另外,影响速度的原因可能还有:1)网速;2)输出图片,有的是直接输出大图,在回复的源微博中如果有图片,总是获取不到缩略图,只好依次判断小图、中图、大图,先有哪个输出哪个。用Label显示文本行间距没法调,只好做了个自定义控件,主要是根据字体大小了增大行距。

界面虽然使用皮肤(ssk),比Window Form原始的界面好看的多,但是比起Adobe的那个AIR还是有差距,或许可以尝试使用WPF。

后续要实现的功能(还没全部看API,有的功能不一定提供API):
1)转发、收藏、回复
2)发帖可以使用表情、可以上传图片
3)关注、粉丝列表
4)查看自己微博、他人微博列表
5)个人的一些配置信息
6)主界面左边想放新浪微博登录后,个人首页右边的内容
... ...
工作量还挺大:(的。

目前经过处理,程序运行后占用的内存在4M-25M这样的一个范围 ,如果不处理会一直攀升,100M、200M、... ... 。
在翻页时,加载显示瞬时CPU占用率能够达到30%左右,一般比较低,5%以下。
滚屏时,CPU占用率一般18%以下,都是瞬时的。
大家看到的数据异步加载使用了多线程,主要是使用了BackgroundWorker。

除了上面的问题,在滚屏时,滚动的内容有点闪,还没找到好的解决办法。

欢迎有兴趣的人一起讨论,提宝贵意见。有经验的人做个指导:)。

最后附上读取新浪微博API的部分代码:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.IO;
usingSystem.Net;
usingMBClient.Entities;
usingSystem.Runtime.Serialization.Json;
usingSystem.Data;
usingSystem.Drawing;

namespaceMBClient.API
{
internalcla***eadDataBySinaAPI
{
internalReadDataBySinaAPI(){}

///summary
///根据用户登录信息登录并读取用户信息到DataSet
///如果用户新浪通行证身份验证成功且用户已经开通微博则返回http状态为200;
///如果是不则返回401的状态和错误信息。此方法用了判断用户身份是否合法且已经开通微博。
///http://open.t.sina.com.cn/wiki/index.php/Account/verify_credentials
summary
///paramname="url"APIURL(XMLFormat)http://api.t.sina.com.cn/account/verify_credentials.xml?source=AppKey
param
///returns/returns
internalstaticDataSetReadXMLDataToDataSet(stringurl)
{
CredentialCachemycache=newCredentialCache();
mycache.Add(newUri(url),"Basic",newNetworkCredential(UserInfo.UserName,UserInfo.Password));
WebRequestmyReq=WebRequest.Create(url);

myReq.Credentials=mycache;
myReq.Headers.Add("Authorization","Basic"+Convert.ToBase64String(newASCIIEncoding().GetBytes(UserInfo.UserNamePassword)));

WebResponsewr=myReq.GetResponse();

DataSetds=newDataSet();
using(StreamreceiveStream=wr.GetResponseStream())
{
StreamReaderreader=newStreamReader(receiveStream,Encoding.UTF8);
//stringcontent=reader.ReadToEnd();
ds.ReadXml(reader);
reader.Close();
}
wr.Close();
returnds;
}

internalstaticstringReadXMLDataToString(stringurl)
{
CredentialCachemycache=newCredentialCache();
mycache.Add(newUri(url),"Basic",newNetworkCredential(UserInfo.UserName,UserInfo.Password));
WebRequestmyReq=WebRequest.Create(url);
myReq.Credentials=mycache;
myReq.Headers.Add("Authorization","Basic"+Convert.ToBase64String(newASCIIEncoding().GetBytes(UserInfo.UserNamePassword)));

WebResponsewr=myReq.GetResponse();
stringstrData=null;
using(StreamreceiveStream=wr.GetResponseStream())
{
StreamReaderreader=newStreamReader(receiveStream,Encoding.UTF8);
strData=reader.ReadToEnd();
reader.Close();
}
wr.Close();
returnstrData;
}

///summary
///以JSON格式字符串返回用户所有关注用户最新n条微博信息。和用户我的首页返回内容相同。
///http://open.t.sina.com.cn/wiki/index.php/Statuses/friends_timeline
summary
///paramname="url"APIURL(JSONFormat)http://api.t.sina.com.cn/statuses/friends_timeline.json?source=AppKey
param
///returns/returns
internalstaticstringReadJsonDataToString(stringurl)
{
CredentialCachemycache=newCredentialCache();
mycache.Add(newUri(url),"Basic",newNetworkCredential(UserInfo.UserName,UserInfo.Password));
WebRequestmyReq=WebRequest.Create(url);

myReq.Credentials=mycache;
myReq.Headers.Add("Authorization","Basic"+Convert.ToBase64String(newASCIIEncoding().GetBytes(UserInfo.UserNamePassword)));

WebResponsewr=myReq.GetResponse();
stringcontent=null;
using(StreamreceiveStream=wr.GetResponseStream())
{
StreamReaderreader=newStreamReader(receiveStream,Encoding.UTF8);
content=reader.ReadToEnd();
reader.Close();
}
wr.Close();
returncontent;
}

///summary
///将JSON格式字符串反序列化为Status集合对象
summary
///paramname="url"同ReadJsonDataToString()/param
///returns/returns
internalstaticListStatusDeserializeJsonToListObject(stringurl)
{
ListStatuslistObj;
using(MemoryStreamstream=newMemoryStream())
{
DataContractJsonSerializerser=newDataContractJsonSerializer(typeof(ListStatus));
StreamWriterwr=newStreamWriter(stream);
stringstrJson=ReadJsonDataToString(url);
wr.Write(strJson);
wr.Flush();
stream.Position=0;
Objectobj=ser.ReadObject(stream);
listObj=(ListStatus)obj;
wr.Close();
}

returnlistObj;
}

///summary
///获取并输出图片
summary
///paramname="imgUrl"图片URL地址/param
///returns/returns
internalstaticImageGetAvatarImage(stringimgUrl)
{
UrimyUri=newUri(imgUrl);
WebRequestwebRequest=WebRequest.Create(myUri);
WebResponsewebResponse=webRequest.GetResponse();
BitmapmyImage=newBitmap(webResponse.GetResponseStream());

return(Image)myImage;
}

///summary
///发表一条微博
summary
///paramname="url"API地址
///http://api.t.sina.com.cn/statuses/update.json
param
///paramname="data"AppKey和微博内容
///"source=123456status="+System.Web.HttpUtility.UrlEncode(微博内容);
param
///returns/returns
internalstaticboolPostBlog(stringurl,stringdata)
{
try
{
System.Net.WebRequestwebRequest=System.Net.WebRequest.Create(url);
System.Net.HttpWebRequesthttpRequest=webRequestasSystem.Net.HttpWebRequest;

System.Net.CredentialCachemyCache=newSystem.Net.CredentialCache();
myCache.Add(newUri(url),"Basic",
newSystem.Net.NetworkCredential(UserInfo.UserName,UserInfo.Password));
httpRequest.Credentials=myCache;
httpRequest.Headers.Add("Authorization","Basic"+Convert.ToBase64String(
newSystem.Text.ASCIIEncoding().GetBytes(UserInfo.UserName+":"+
UserInfo.Password)));

httpRequest.Method="POST";
httpRequest.ContentType="application/x-www-form-urlencoded";
System.Text.Encodingencoding=System.Text.Encoding.UTF8;//System.Text.Encoding.ASCII
byte[]bytesToPost=encoding.GetBytes(data);//System.Web.HttpUtility.UrlEncode(data)
httpRequest.ContentLength=bytesToPost.Length;
System.IO.StreamrequestStream=httpRequest.GetRequestStream();
requestStream.Write(bytesToPost,0,bytesToPost.Length);
requestStream.Close();
returntrue;
}
catch
{
returnfalse;
}
}
}
}




感谢博客园的这篇文章,参考价值很大:http://www.cnblogs.com/cmt/archive/2010/05/13/1733904.html
注:AppKey以内置,是我申请的,每IP每用户一小时内限调用350次API。
PS:别拿来干坏事... ...

附一条完整的博客内容数据(XML格式)
status
created_atWedNov2421:24:04+08002010/created_at
id3858478793/id
text很好的推论。//@白手套的小咪:这是罗永浩流传最广的名言之一。给他改一改:没有哪只鸟来到世间是为了躲枪子儿的。所以,手上拿着枪,并用枪打出头鸟这样的道理教训人的那些人,一定要明白,每只鸟都梦想出头,你打到老死也打不完的。/text
source
ahref="http://t.sina.com.cn"新浪微博/a
/source
favoritedfalse/favorited
truncatedfalse/truncated
geo/
in_reply_to_status_id/in_reply_to_status_id
in_reply_to_user_id/in_reply_to_user_id
in_reply_to_screen_name/in_reply_to_screen_name
user
id1197161814/id
screen_name李开复/screen_name
name李开复/name
province11/province
city1000/city
location北京/location
description创新工场CEO/description
urlhttp://blog.sina.com.cn/kaifulee/url
profile_image_urlhttp://tp3.sinaimg.cn/1197161814/50/1290146312/1/profile_image_url
domainkaifulee/domain
genderm/gender
followers_count2384862/followers_count
friends_count132/friends_count
statuses_count1039/statuses_count
favourites_count2/favourites_count
created_atFriAug2800:00:00+08002009/created_at
followingfalse/following
verifiedtrue/verified
allow_all_act_msgfalse/allow_all_act_msg
geo_enabledtrue/geo_enabled
/user
retweeted_status
created_atWedNov2417:35:07+08002010/created_at
id3853748245/id
text希望那些喜欢用枪打出头鸟这样的道理教训年轻人,并且因此觉得自己很成熟的中国人有一天能够明白这样一个事实,那就是:有的鸟来到世间,是为了做它该做的事,而不是专门躲枪子儿的。/text
source
ahref="http://t.sina.com.cn"新浪微博/a
/source
favoritedfalse/favorited
truncatedfalse/truncated
geo/
in_reply_to_status_id/in_reply_to_status_id
in_reply_to_user_id/in_reply_to_user_id
in_reply_to_screen_name/in_reply_to_screen_name
user
id1672204023/id
screen_nameRan-w/screen_name
nameRan-w/name
province11/province
city5/city
location北京朝阳区/location
description什么都不知道总比什么都知道要好。慢慢经历那个不知道的未来。/description
urlhttp://blog.sina.com.cn/wongr11/url
profile_image_urlhttp://tp4.sinaimg.cn/1672204023/50/1290577674/0/profile_image_url
domainwongr/domain
genderf/gender
followers_count757/followers_count
friends_count178/friends_count
statuses_count521/statuses_count
favourites_count4/favourites_count
created_atMonDec2100:00:00+08002009/created_at
followingfalse/following
verifiedfalse/verified
allow_all_act_msgfalse/allow_all_act_msg
geo_enabledtrue/geo_enabled
/user
/retweeted_status
/status

status是博主发表的博文,retweeted_status是回复或者转发了哪条博文。


下载微博客户端