新浪微博PC客户端(DotNet WinForm版)—— 初探

最近兴趣使然尝试了一下使用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的部分代码:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using MBClient.Entities;
using System.Runtime.Serialization.Json;
using System.Data;
using System.Drawing;

namespace MBClient.API
{
    
internal class ReadDataBySinaAPI
    {
        
internal ReadDataBySinaAPI() { }

        
/// <summary>
        
/// 根据用户登录信息登录并读取用户信息到DataSet
        
/// 如果用户新浪通行证身份验证成功且用户已经开通微博则返回 http状态为 200;
        
/// 如果是不则返回401的状态和错误信息。此方法用了判断用户身份是否合法且已经开通微博。
        
/// http://open.t.sina.com.cn/wiki/index.php/Account/verify_credentials
        
/// </summary>
        
/// <param name="url">API URL (XML Format)http://api.t.sina.com.cn/account/verify_credentials.xml?source=AppKey
        
/// </param>
        
/// <returns></returns>
        internal static DataSet ReadXMLDataToDataSet(string url)
        {
            CredentialCache mycache 
= new CredentialCache();
            mycache.Add(
new Uri(url), "Basic"new NetworkCredential(UserInfo.UserName, UserInfo.Password));
            WebRequest myReq 
= WebRequest.Create(url);

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

            WebResponse wr 
= myReq.GetResponse();

            DataSet ds 
= new DataSet();
            
using (Stream receiveStream = wr.GetResponseStream())
            {
                StreamReader reader 
= new StreamReader(receiveStream, Encoding.UTF8);
                
//string content = reader.ReadToEnd();
                ds.ReadXml(reader);
                reader.Close();
            }
            wr.Close();
            
return ds;
        }

        
internal static string ReadXMLDataToString(string url)
        {
            CredentialCache mycache 
= new CredentialCache();
            mycache.Add(
new Uri(url), "Basic"new NetworkCredential(UserInfo.UserName, UserInfo.Password));
            WebRequest myReq 
= WebRequest.Create(url);
            myReq.Credentials 
= mycache;
            myReq.Headers.Add(
"Authorization""Basic " + Convert.ToBase64String(new ASCIIEncoding().GetBytes(UserInfo.UserNamePassword)));

            WebResponse wr 
= myReq.GetResponse();
            
string strData = null;
            
using (Stream receiveStream = wr.GetResponseStream())
            {
                StreamReader reader 
= new StreamReader(receiveStream, Encoding.UTF8);
                strData 
= reader.ReadToEnd();
                reader.Close();
            }
            wr.Close();
            
return strData;
        }

        
/// <summary>
        
/// 以JSON格式字符串返回用户所有关注用户最新n条微博信息。和用户“我的首页”返回内容相同。
        
/// http://open.t.sina.com.cn/wiki/index.php/Statuses/friends_timeline
        
/// </summary>
        
/// <param name="url">API URL (JSON Format) http://api.t.sina.com.cn/statuses/friends_timeline.json?source=AppKey
        
/// </param>
        
/// <returns></returns>
        internal static string ReadJsonDataToString(string url)
        {
            CredentialCache mycache 
= new CredentialCache();
            mycache.Add(
new Uri(url), "Basic"new NetworkCredential(UserInfo.UserName, UserInfo.Password));
            WebRequest myReq 
= WebRequest.Create(url);

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

            WebResponse wr 
= myReq.GetResponse();
            
string content = null;
            
using (Stream receiveStream = wr.GetResponseStream())
            {
                StreamReader reader 
= new StreamReader(receiveStream, Encoding.UTF8);
                content 
= reader.ReadToEnd();
                reader.Close();
            }
            wr.Close();
            
return content;
        }

        
/// <summary>
        
/// 将JSON格式字符串反序列化为Status集合对象
        
/// </summary>
        
/// <param name="url">同ReadJsonDataToString()</param>
        
/// <returns></returns>
        internal static List<Status> DeserializeJsonToListObject(string url)
        {
            List
<Status> listObj;
            
using (MemoryStream stream = new MemoryStream())
            {
                DataContractJsonSerializer ser 
= new DataContractJsonSerializer(typeof(List<Status>));
                StreamWriter wr 
= new StreamWriter(stream);
                
string strJson = ReadJsonDataToString(url);
                wr.Write(strJson);
                wr.Flush();
                stream.Position 
= 0;
                Object obj 
= ser.ReadObject(stream);
                listObj 
= (List<Status>)obj;
                wr.Close();
            }

            
return listObj;
        }

        
/// <summary>
        
/// 获取并输出图片
        
/// </summary>
        
/// <param name="imgUrl">图片URL地址</param>
        
/// <returns></returns>
        internal static Image GetAvatarImage(string imgUrl)
        {
            Uri myUri 
= new Uri(imgUrl);
            WebRequest webRequest 
= WebRequest.Create(myUri);
            WebResponse webResponse 
= webRequest.GetResponse();
            Bitmap myImage 
= new Bitmap(webResponse.GetResponseStream());

            
return (Image)myImage;
        }

        
/// <summary>
        
/// 发表一条微博
        
/// </summary>
        
/// <param name="url">API地址
        
/// http://api.t.sina.com.cn/statuses/update.json
        
/// </param>
        
/// <param name="data">AppKey和微博内容
        
/// "source=123456&status=" + System.Web.HttpUtility.UrlEncode(微博内容);
        
/// </param>
        
/// <returns></returns>
        internal static bool PostBlog(string url,string data)
        {
            
try
            {
                System.Net.WebRequest webRequest 
= System.Net.WebRequest.Create(url);
                System.Net.HttpWebRequest httpRequest 
= webRequest as System.Net.HttpWebRequest;

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

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

 

 

 

感谢博客园的这篇文章,参考价值很大:http://www.cnblogs.com/cmt/archive/2010/05/13/1733904.html 

注:AppKey以内置,是我申请的,每IP每用户一小时内限调用350次API。

PS:别拿来干坏事... ... 

 

附一条完整的博客内容数据(XML格式)

<status>
    
<created_at>Wed Nov 24 21:24:04 +0800 2010</created_at>
    
<id>3858478793</id>
    
<text>很好的推论。 //@白手套的小咪:这是罗永浩流传最广的名言之一。给他改一改:没有哪只鸟来到世间是为了躲枪子儿的。所以,手上拿着枪,并用“枪打出头鸟”这样的道理教训人的那些人,一定要明白,每只鸟都梦想出头,你打到老死也打不完的。</text>
    
<source>
      
<href="http://t.sina.com.cn">新浪微博</a>
    
</source>
    
<favorited>false</favorited>
    
<truncated>false</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>
      
<id>1197161814</id>
      
<screen_name>李开复</screen_name>
      
<name>李开复</name>
      
<province>11</province>
      
<city>1000</city>
      
<location>北京</location>
      
<description>创新工场CEO</description>
      
<url>http://blog.sina.com.cn/kaifulee</url>
      
<profile_image_url>http://tp3.sinaimg.cn/1197161814/50/1290146312/1</profile_image_url>
      
<domain>kaifulee</domain>
      
<gender>m</gender>
      
<followers_count>2384862</followers_count>
      
<friends_count>132</friends_count>
      
<statuses_count>1039</statuses_count>
      
<favourites_count>2</favourites_count>
      
<created_at>Fri Aug 28 00:00:00 +0800 2009</created_at>
      
<following>false</following>
      
<verified>true</verified>
      
<allow_all_act_msg>false</allow_all_act_msg>
      
<geo_enabled>true</geo_enabled>
    
</user>
    
<retweeted_status>
      
<created_at>Wed Nov 24 17:35:07 +0800 2010</created_at>
      
<id>3853748245</id>
      
<text>“希望那些喜欢用“枪打出头鸟”这样的道理教训年轻人,并且因此觉得自己很成熟的中国人有一天能够明白这样一个事实,那就是:有的鸟来到世间,是为了做它该做的事,而不是专门躲枪子儿的。”</text>
      
<source>
        
<href="http://t.sina.com.cn">新浪微博</a>
      
</source>
      
<favorited>false</favorited>
      
<truncated>false</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>
        
<id>1672204023</id>
        
<screen_name>Ran-w</screen_name>
        
<name>Ran-w</name>
        
<province>11</province>
        
<city>5</city>
        
<location>北京 朝阳区</location>
        
<description>什么都不知道总比什么都知道要好。慢慢经历那个不知道的未来。</description>
        
<url>http://blog.sina.com.cn/wongr11</url>
        
<profile_image_url>http://tp4.sinaimg.cn/1672204023/50/1290577674/0</profile_image_url>
        
<domain>wongr</domain>
        
<gender>f</gender>
        
<followers_count>757</followers_count>
        
<friends_count>178</friends_count>
        
<statuses_count>521</statuses_count>
        
<favourites_count>4</favourites_count>
        
<created_at>Mon Dec 21 00:00:00 +0800 2009</created_at>
        
<following>false</following>
        
<verified>false</verified>
        
<allow_all_act_msg>false</allow_all_act_msg>
        
<geo_enabled>true</geo_enabled>
      
</user>
    
</retweeted_status>
  
</status>

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

 

 

下载微博客户端 


posted on 2010-11-26 23:43  Ferry  阅读(7822)  评论(23编辑  收藏  举报

导航