花了几天时间,消耗了九牛六虎之力,新浪微博大部分API已经封装,但有部分API实在太难封装。

说起这封装,我必须严重地、从人品和技术层面鄙视一下新浪的程序员,实在太菜了。估计菜鸟都被大企业吸收了,菜到连面向对象都不懂。建议新浪的菜菜们向淘宝学习下,人家淘宝还同时有XML和JSON两种数据格式。

同样的内容,返回的JSON对象居然会出现不同结构,更可恶的,像公共API中获取城市列表,国家区域代码列表的返回结果,实在让人不得不发笑。那些JSON用JS读起来都困难,更何况要进行封装呢。根本没法封装,因此在论坛上抱怨的人不少,可是新浪官方呢,置之不管,就当没看见一样,看来,大企业就是这点水平。

题外话不多说,回归主题。除了权限不够和无法封装的API外,其余的API都封装了。先上一张代码地图。

这图有点像蜘蛛网,非常有美感,可惜是静止的,如果会动的,一定很好看。

我不可能把代码一行一行地向大家讲述,因为这样做会相当恶心。现在菜菜们都整天拿开源来装逼,所以,为了迎合广大菜菜所说的所谓“趋势”,我也决定把我这份乱七八糟的代码向全人类公开。

下载地址:http://vdisk.weibo.com/s/z7iFc2gCCwC1b

接下来,就给大家说说原理和思路,这才是编程的关键。

1、不管是微博API还是其他的开放平台的API,都有N多共同点。首先,用户要用自己的帐号登录,然后由用户决定是否授权给我们的应用程序,如果用户已同意授权,我们会得到一个授权码(Code)。

2、拿到Code后,我们要用这个Code来换取一个Access Token,就相当于用户授予我们一把钥匙,我们再用这把钥匙去开启库房的门,然后取出一个临时令牌,就好比在汉代,大臣代表天子巡视诸侯国时,手里要持着天子的符节一样。有了Token,我们的应用才能进行一系列操作。

微博的API的每一次调用,其实就是一轮HTTP请求-响应的往返过程,说白了,就是一问一答,我们调用API是问,服务器完成相关处理后返回结果给我们,为答。我们也知道,HTTP发送数据,用得最多的两种方法是GET和POST,至于是GET还是POST,我们按照API文档的说明去干就行了。

而对于服务器返回的数据(JSON),我是通过数据协定的形式来进行封装的。比如:

一个表示用户信息的回复JSON如下:

  1. {
  2. "id": 1404376560,
  3. "screen_name": "zaku",
  4. "name": "zaku",
  5. "province": "11",
  6. "city": "5",
  7. "location": "北京 朝阳区",
  8. "description": "人生五十年,乃如梦如幻;有生斯有死,壮士复何憾。",
  9. "url": "http://blog.sina.com.cn/zaku",
  10. "profile_image_url": "http://tp1.sinaimg.cn/1404376560/50/0/1",
  11. "domain": "zaku",
  12. "gender": "m",
  13. "followers_count": 1204,
  14. "friends_count": 447,
  15. "statuses_count": 2908,
  16. "favourites_count": 0,
  17. "created_at": "Fri Aug 28 00:00:00 +0800 2009",
  18. "following": false,
  19. "allow_all_act_msg": false,
  20. "geo_enabled": true,
  21. "verified": false,
  22. "status": {
  23. "created_at": "Tue May 24 18:04:53 +0800 2011",
  24. "id": 11142488790,
  25. "text": "我的相机到了。",
  26. "source": "<a href="http://weibo.com" rel="nofollow">新浪微博</a>",
  27. "favorited": false,
  28. "truncated": false,
  29. "in_reply_to_status_id": "",
  30. "in_reply_to_user_id": "",
  31. "in_reply_to_screen_name": "",
  32. "geo": null,
  33. "mid": "5610221544300749636",
  34. "annotations": [],
  35. "reposts_count": 5,
  36. "comments_count": 8
  37. },
  38. "allow_all_comment": true,
  39. "avatar_large": "http://tp1.sinaimg.cn/1404376560/180/0/1",
  40. "verified_reason": "",
  41. "follow_me": false,
  42. "online_status": 0,
  43. "bi_followers_count": 215
  44. }

我们可以根据JSON中每个成员的名字,用代码封装成一个类。

  1. /// <summary>
  2. /// 用户实体
  3. /// </summary>
  4. [DataContract]
  5. public class UserInfo
  6. {
  7. /// <summary>
  8. /// 用户UID
  9. /// </summary>
  10. [DataMember(Name = "id")]
  11. public long ID { get; set; }
  12.  
  13. /// <summary>
  14. /// 字符串型的用户UID
  15. /// </summary>
  16. [DataMember(Name = "idstr")]
  17. public string IDStr { get; set; }
  18.  
  19. /// <summary>
  20. /// 用户昵称
  21. /// </summary>
  22. [DataMember(Name = "screen_name")]
  23. public string ScreenName { get; set; }
  24.  
  25. /// <summary>
  26. /// 友好显示名称
  27. /// </summary>
  28. [DataMember(Name = "name")]
  29. public string Name { get; set; }
  30.  
  31. /// <summary>
  32. /// 用户所在省级ID
  33. /// </summary>
  34. [DataMember(Name = "province")]
  35. public int Province { get; set; }
  36.  
  37. /// <summary>
  38. /// 用户所在城市ID
  39. /// </summary>
  40. [DataMember(Name = "city")]
  41. public int City { get; set; }
  42.  
  43. /// <summary>
  44. /// 用户所在地
  45. /// </summary>
  46. [DataMember(Name = "location")]
  47. public string Location { get; set; }
  48.  
  49. /// <summary>
  50. /// 用户个人描述
  51. /// </summary>
  52. [DataMember(Name = "description")]
  53. public string Description { get; set; }
  54.  
  55. /// <summary>
  56. /// 用户博客地址
  57. /// </summary>
  58. [DataMember(Name = "url")]
  59. public string Url { get; set; }
  60.  
  61. /// <summary>
  62. /// 用户头像地址(中图),50×50像素
  63. /// </summary>
  64. [DataMember(Name = "profile_image_url")]
  65. public string ProfileImageUrl { get; set; }
  66.  
  67. /// <summary>
  68. /// 用户的微博统一URL地址
  69. /// </summary>
  70. [DataMember(Name = "profile_url")]
  71. public string ProfileUrl { get; set; }
  72.  
  73. /// <summary>
  74. /// 用户的个性化域名
  75. /// </summary>
  76. [DataMember(Name = "domain")]
  77. public string Domain { get; set; }
  78.  
  79. /// <summary>
  80. /// 用户的微号
  81. /// </summary>
  82. [DataMember(Name = "weihao")]
  83. public string WeiHao { get; set; }
  84.  
  85. /// <summary>
  86. /// 性别,m:男、f:女、n:未知
  87. /// </summary>
  88. [DataMember(Name = "gender")]
  89. public string Gender { get; set; }
  90.  
  91. /// <summary>
  92. /// 粉丝数
  93. /// </summary>
  94. [DataMember(Name = "followers_count")]
  95. public int FollowersCount { get; set; }
  96.  
  97. /// <summary>
  98. /// 关注数
  99. /// </summary>
  100. [DataMember(Name = "friends_count")]
  101. public int FriendsCount { get; set; }
  102.  
  103. /// <summary>
  104. /// 微博数
  105. /// </summary>
  106. [DataMember(Name = "statuses_count")]
  107. public int StatusesCount { get; set; }
  108.  
  109. /// <summary>
  110. /// 收藏数
  111. /// </summary>
  112. [DataMember(Name = "favourites_count")]
  113. public int FavouritesCount { get; set; }
  114.  
  115. /// <summary>
  116. /// 用户创建(注册)时间
  117. /// </summary>
  118. [DataMember(Name = "created_at")]
  119. public string CreatedAt { get; set; }
  120.  
  121. /// <summary>
  122. /// 暂未支持
  123. /// </summary>
  124. [DataMember(Name = "following")]
  125. public bool Following { get; set; }
  126.  
  127. /// <summary>
  128. /// 是否允许所有人给我发私信,true:是,false:否
  129. /// </summary>
  130. [DataMember(Name = "allow_all_act_msg")]
  131. public bool AllowAllActMsg { get; set; }
  132.  
  133. /// <summary>
  134. /// 是否允许标识用户的地理位置,true:是,false:否
  135. /// </summary>
  136. [DataMember(Name = "geo_enabled")]
  137. public bool GeoEnabled { get; set; }
  138.  
  139. /// <summary>
  140. /// 是否是微博认证用户,即加V用户,true:是,false:否
  141. /// </summary>
  142. [DataMember(Name = "verified")]
  143. public bool Verified { get; set; }
  144.  
  145. /// <summary>
  146. /// 暂未支持
  147. /// </summary>
  148. [DataMember(Name = "verified_type")]
  149. public int VerifiedType { get; set; }
  150.  
  151. /// <summary>
  152. /// 用户备注信息
  153. /// </summary>
  154. [DataMember(Name = "remark")]
  155. public string Remark { get; set; }
  156.  
  157. /// <summary>
  158. /// 用户的最近一条微博信息
  159. /// </summary>
  160. [DataMember(Name = "status")]
  161. public Status Status { get; set; }
  162.  
  163. /// <summary>
  164. /// 当对象数据未返回完整微博信息时,将填充该字段
  165. /// </summary>
  166. [DataMember(Name = "status_id")]
  167. public long StatusID { get; set; }
  168.  
  169. /// <summary>
  170. /// 是否允许所有人对我的微博进行评论,true:是,false:否
  171. /// </summary>
  172. [DataMember(Name = "allow_all_comment")]
  173. public bool AllowAllComment { get; set; }
  174.  
  175. /// <summary>
  176. /// 用户头像地址(大图),180×180像素
  177. /// </summary>
  178. [DataMember(Name = "avatar_large")]
  179. public string AvatarLarge { get; set; }
  180.  
  181. /// <summary>
  182. /// 用户头像地址(高清),高清头像原图
  183. /// </summary>
  184. [DataMember(Name = "avatar_hd")]
  185. public string AvatarHd { get; set; }
  186.  
  187. /// <summary>
  188. /// 认证原因
  189. /// </summary>
  190. [DataMember(Name = "verified_reason")]
  191. public string VerifiedReason { get; set; }
  192.  
  193. /// <summary>
  194. /// 该用户是否关注当前登录用户,true:是,false:否
  195. /// </summary>
  196. [DataMember(Name = "follow_me")]
  197. public bool FollowMe { get; set; }
  198.  
  199. /// <summary>
  200. /// 用户的在线状态,0:不在线、1:在线
  201. /// </summary>
  202. [DataMember(Name = "online_status")]
  203. public int OnlineStatus { get; set; }
  204.  
  205. /// <summary>
  206. /// 用户的互粉数
  207. /// </summary>
  208. [DataMember(Name = "bi_followers_count")]
  209. public int BiFollowersCount { get; set; }
  210.  
  211. /// <summary>
  212. /// 用户当前的语言版本,zh-cn:简体中文,zh-tw:繁体中文,en:英语
  213. /// </summary>
  214. [DataMember(Name = "lang")]
  215. public string Lang { get; set; }
  216. }

其中,Status属性表示用户发表的最新一条微博,它由一个表示微博的对象构成。

  1. {
  2. "created_at": "Tue May 24 18:04:53 +0800 2011",
  3. "id": 11142488790,
  4. "text": "我的相机到了。",
  5. "source": "<a href="http://weibo.com" rel="nofollow">新浪微博</a>",
  6. "favorited": false,
  7. "truncated": false,
  8. "in_reply_to_status_id": "",
  9. "in_reply_to_user_id": "",
  10. "in_reply_to_screen_name": "",
  11. "geo": null,
  12. "mid": "5610221544300749636",
  13. "annotations": [],
  14. "reposts_count": 5,
  15. "comments_count": 8
  16. }

同样,我们也用一个数据协定类来封装它,表示一条微博的信息。

  1. /// <summary>
  2. /// 微博实体
  3. /// </summary>
  4. [DataContract]
  5. public class Status
  6. {
  7. /// <summary>
  8. /// 微博创建时间
  9. /// </summary>
  10. [DataMember(Name = "created_at")]
  11. public string CreateAt { get; set; }
  12.  
  13. /// <summary>
  14. /// 微博ID
  15. /// </summary>
  16. [DataMember(Name = "id")]
  17. public Int64 ID { get; set; }
  18.  
  19. /// <summary>
  20. /// 微博MID
  21. /// </summary>
  22. [DataMember(Name = "mid")]
  23. public Int64 MID { set; get; }
  24.  
  25. /// <summary>
  26. /// 字符串型的微博ID
  27. /// </summary>
  28. [DataMember(Name = "idstr")]
  29. public string IDStr { get; set; }
  30.  
  31. /// <summary>
  32. /// 微博信息内容
  33. /// </summary>
  34. [DataMember(Name = "text")]
  35. public string Text { get; set; }
  36.  
  37. /// <summary>
  38. /// 微博来源
  39. /// </summary>
  40. [DataMember(Name = "source")]
  41. public string Source { get; set; }
  42.  
  43. /// <summary>
  44. /// 是否已收藏,true:是,false:否
  45. /// </summary>
  46. [DataMember(Name = "favorited")]
  47. public bool Favorited { get; set; }
  48.  
  49. /// <summary>
  50. /// 是否被截断,true:是,false:否
  51. /// </summary>
  52. [DataMember(Name = "truncated")]
  53. public bool Truncated { get; set; }
  54.  
  55. /// <summary>
  56. /// (暂未支持)回复ID
  57. /// </summary>
  58. [DataMember(Name = "in_reply_to_status_id")]
  59. public string InReplyToStatusID { get; set; }
  60.  
  61. /// <summary>
  62. /// (暂未支持)回复人UID
  63. /// </summary>
  64. [DataMember(Name = "in_reply_to_user_id")]
  65. public string InReplyToUserID { get; set; }
  66.  
  67. /// <summary>
  68. /// (暂未支持)回复人昵称
  69. /// </summary>
  70. [DataMember(Name = "in_reply_to_screen_name")]
  71. public string InReplyToScreenName { get; set; }
  72.  
  73. /// <summary>
  74. /// 缩略图片地址
  75. /// </summary>
  76. [DataMember(Name = "thumbnail_pic")]
  77. public string ThumbnailPic { get; set; }
  78.  
  79. /// <summary>
  80. /// 中等尺寸图片地址
  81. /// </summary>
  82. [DataMember(Name = "bmiddle_pic")]
  83. public string BmiddlePic { get; set; }
  84.  
  85. /// <summary>
  86. /// 原始图片地址
  87. /// </summary>
  88. [DataMember(Name = "original_pic")]
  89. public string OriginalPic { get; set; }
  90.  
  91. /// <summary>
  92. /// 地理信息
  93. /// </summary>
  94. [DataMember(Name = "geo")]
  95. public GeoInfo Geo { get; set; }
  96.  
  97. /// <summary>
  98. /// 微博作者的用户信息
  99. /// </summary>
  100. [DataMember(Name = "user")]
  101. public UserInfo User { get; set; }
  102.  
  103. /// <summary>
  104. /// 被转发的原微博信息字段,当该微博为转发微博时返回
  105. /// </summary>
  106. [DataMember(Name = "retweeted_status")]
  107. public Status RetweetedStatus { get; set; }
  108.  
  109. /// <summary>
  110. /// 转发数
  111. /// </summary>
  112. [DataMember(Name = "reposts_count")]
  113. public int RepostsCount { get; set; }
  114.  
  115. /// <summary>
  116. /// 评论数
  117. /// </summary>
  118. [DataMember(Name = "comments_count")]
  119. public int CommentsCount { get; set; }
  120.  
  121. /// <summary>
  122. /// 表态数
  123. /// </summary>
  124. [DataMember(Name = "attitudes_count")]
  125. public int AttitudesCount { get; set; }
  126.  
  127. /// <summary>
  128. /// 暂未支持
  129. /// </summary>
  130. [DataMember(Name = "mlevel")]
  131. public int Mlevel { get; set; }
  132.  
  133. /// <summary>
  134. /// 微博的可见性及指定可见分组信息。该object中type取值,0:普通微博,1:私密微博,3:指定分组微博,4:密友微博;list_id为分组的组号
  135. /// </summary>
  136. [DataMember(Name = "visible")]
  137. public dynamic Visible { get; set; }
  138.  
  139. /// <summary>
  140. /// 微博配图地址。多图时返回多图链接。无配图返回“[]”
  141. /// </summary>
  142. [DataMember(Name = "pic_urls")]
  143. public dynamic PicUrls { get; set; }
  144.  
  145. /// <summary>
  146. /// 微博流内的推广微博ID
  147. /// </summary>
  148. [DataMember(Name = "ad")]
  149. public dynamic Ad { get; set; }
  150.  
  151. /// <summary>
  152. /// 当不返回user字段时,此属性可填充UID
  153. /// </summary>
  154. [DataMember(Name = "uid")]
  155. public long Uid { get; set; }
  156. }

我们在接收到服务器的回应后,直接对数据进行反序列化,就可以得到这些数据的封装实例,面向对象,操作起来更方便。这些被封装的基本类型,我都放到了Models目录下。为了实现反序列化,我定义了一个类,公开一个静态方法,以便从JSON数据生成对象实例。

  1. public class JsonSerializeHelper
  2. {
  3. public static T ReadDataFromJson<T>(Stream inStream)
  4. {
  5. DataContractJsonSerializer s = new DataContractJsonSerializer(typeof(T));
  6. return (T)s.ReadObject(inStream);
  7. }
  8. }

因为我们要反序列化的类型是多种多样的,无法定论,因此,这里使用泛型参数较合适。具体反序列化为哪种类型的实例,在运行时决定。

由于每个API的调用都是一轮HTTP请求/回应,无论你调用哪个API都一样,这个行为是通过的,所以,我就统一进行提取,并使用.NET 4.5新增的HttpClient类来处理,这个类很强大,把一些不太好处理的HTTP请求都为我们封装好了,尤其是像form-data这种POST的数据,如MultiPart form data这些,尤其是向服务器上传文件时较容易处理,省去许多不必要的机械性工作,将人类从无必要的代码中解放出来,极大地提高生产率,这是.NET最牛X的地方。

  1. internal static async Task<TResult> SendRequestWithMultipartFormDataAsync<TResult>(string relateUrl, IDictionary<string, object> parms, string filename)
  2. {
  3. Uri reqUri = new Uri(API_BASE_RUI);
  4. reqUri = new Uri(reqUri, relateUrl);
  5. TResult result = default(TResult);
  6. using (HttpClient client = new HttpClient())
  7. {
  8. string b = "---------------------" + DateTime.Now.Ticks.ToString("x");
  9. MultipartFormDataContent formData = new MultipartFormDataContent(b);
  10. foreach (var pair in parms)
  11. {
  12. string str = pair.Value as string;
  13. if (str != null)
  14. {
  15. StringContent stringContent = new StringContent(pair.Value as string);
  16. formData.Add(stringContent, pair.Key);
  17. }
  18.  
  19. Stream stream = pair.Value as Stream;
  20. if (stream != null)
  21. {
  22. StreamContent streamContent = new StreamContent(stream);
  23. formData.Add(streamContent, pair.Key, filename);
  24. }
  25. }
  26. var response = await client.PostAsync(reqUri, formData);
  27. if (response.IsSuccessStatusCode)
  28. {
  29. using (Stream backstream = await response.Content.ReadAsStreamAsync())
  30. {
  31. result = JsonSerializeHelper.ReadDataFromJson<TResult>(backstream);
  32. }
  33. }
  34. else
  35. {
  36. ErrorData err = null;
  37. using (Stream errstream = await response.Content.ReadAsStreamAsync())
  38. {
  39. err = JsonSerializeHelper.ReadDataFromJson<ErrorData>(errstream);
  40. }
  41. throw new WeiboException(err);
  42. }
  43. }
  44.  
  45. return result;
  46. }
  47.  
  48. internal static async Task<string> HttpGetStringAsync(string relateUrl)
  49. {
  50. Uri base_uri = new Uri(API_BASE_RUI);
  51. Uri request_uri = new Uri(base_uri, relateUrl);
  52. string result = "";
  53. using (HttpClient client = new HttpClient())
  54. {
  55. result = await client.GetStringAsync(request_uri);
  56. }
  57. return result;
  58. }
  59.  
  60. internal static async Task<Stream> HttpGetStreamAsync(string relateUrl)
  61. {
  62. Uri base_uri = new Uri(API_BASE_RUI);
  63. base_uri = new Uri(base_uri, relateUrl);
  64. Stream streamres = null;
  65. using (HttpClient client = new HttpClient())
  66. {
  67. var streamtmp = await client.GetStreamAsync(base_uri);
  68. streamres = new MemoryStream();
  69. streamtmp.CopyTo(streamres);
  70. streamtmp.Dispose();
  71. }
  72. return streamres;
  73. }

上面仅仅列举了一两个方法,详细的代码大家可以下载源代码看。

在我发布的解决方案中,有一个项目名为WeiboTest,它是一个单元测试项目,是我在封装API过程用来测试调用而写的。

这个SDK可用在桌面程序和Web应用程序中,对于WP和Store App,我一开始是考虑建一个可移植的Portable项目的,但由于我仅在试水阶段,后来就没考虑移植了,其实绝大部分代码是可以通用的,但有一小部分不行,比如文件操作就不能通用,因为WP上的文件是通过独立存储来处理的,而不像桌面环境中那样使用物理文件来处理。等代码完善后,我会考虑将它改为一个通用类库。

下面就介绍一下我封装了哪些API。

一、登录授权部分我合为一体了,

不使用新浪弹出的授权页面,而是直接模拟用户登录时POST的数据,直接获取Code,然后调用OAuth2的API换取Token。同时还公开用于取消授权的RevokeOAuth2Async。

二、微博模块。

  1、获取最近的公共微博列表,

  2、获取好友/关注人/自己的最新微博列表。

  3、获取与当前登录用户相互关注的用户的最新微博列表。

  4、获取某条微博的转发列表。

  5、获取@当前用户 的微博列表。

  6、根据微博ID获取单条微博的详细信息。

  7、批量获取微博的转发数和评论数。

  8、微博ID和MID的相互获取。

  9、获取微博官方表情列表。

  10、转发微博。

  11、发表微博 / 发表带图片的微博。

  12、删除微博。

三、评论模块。

  1、获取某条微博的评论列表。

  2、获取当前登录用户所发表的评论列表。

  3、获取当前用户接收到的评论列表。

  4、获取@当前用户的评论列表。

  5、批量获取评论列表。

  6、发表评论。

  7、删除指定评论。

  8、批量删除评论。

  9、回复某条评论。

四、帐号模块。

  1、获取当前用户的隐私设置信息。是设置项,不是获取隐私,别想歪了。

  2、获取学校列表,这个好像没什么用。

五、关系模块。

  1、获取当前登录用户的关注列表。

  2、获取两个用户之间的共同关注人列表。比如我关注了A,而C也关注了A,因而我和C的共同关注人就是A。

  3、获取用户的相互关注人列表。也就是有哪些人跟我互粉。

  4、获取当前用户的粉丝列表。即哪些人关注了我。

  5、获取用户的活跃粉丝列表。这个不知道干什么的,反正我测试的时候返回空。

  6、获取当前登录用户关注列表中同时关注了某用户的列表。比如,我关注了A,获取我关注的用户中也关注了A的用户列表。

  7、关注一位用户。

  8、取消关注某位用户。

六、用户模块。

  1、根据用户ID获取用户信息。

  2、批量获取用户的关注数、粉丝数、微博数。

  3、通过个性域名获取用户信息以及最新发表的一条微博。

七、短链接模块。

  1、长链接转为短链接。

  2、短链接转为长链接。

八、地理位置模块。

  1、根据IP地址返回地理信息。

  2、根据具体地址返回地理坐标信息。

  3、根据地理坐标(经度,纬度)返回地址信息。

这个SDK不算很完美,由于新浪的程序员比较菜,有些JSON数据结构相当不合理,暂时无法封装,等到哪天我想到解决方法后再补充。

新浪微博SDK开发(1):总述的更多相关文章

  1. 新浪微博SDK开发(2):上传图片的技术难点

    在微博模块中,有一个API是可以发表带一张图片的微博的,当然提交方式是POST.在封装的时候,可能会遇到一个难点——如何上传图片? 要POST微博的同时带有图片,POST的内容必须为MultiPart ...

  2. 基于ASP.Net Core开发一套通用后台框架记录-(总述)

    写在前面 本系列博客是本人在学习的过程中搭建学习的记录,如果对你有所帮助那再好不过.如果您有发现错误,请告知我,我会第一时间修改. 前期我不会公开源码,我想是一点点敲代码,不然复制.粘贴那就没意思了. ...

  3. 微信公众账号 Senparc.Weixin.MP SDK 开发教程 索引

    Senparc.Weixin.MP SDK从一开始就坚持开源的状态,这个过程中得到了许多朋友的认可和支持. 目前SDK已经达到比较稳定的版本,这个过程中我觉得有必要整理一些思路和经验,和大家一起分享. ...

  4. 三国杀3v3心法——总述篇

    昔日,独孤求败前辈精研剑法,将其中奥妙化为独孤九剑,破尽天下武功.其中开篇总诀式提纲挈领,从宏观的层面阐述剑道,是领悟后面八式的基石,而之后各式则深入微观,可各破一类具体的武功.笔者亦曾苦心研究三国杀 ...

  5. 【转】微信公众账号 Senparc.Weixin.MP SDK 开发教程 索引

    微信公众账号 Senparc.Weixin.MP SDK 开发教程 索引 Senparc.Weixin.MP SDK从一开始就坚持开源的状态,这个过程中得到了许多朋友的认可和支持. 目前SDK已经达到 ...

  6. Web应用程序系统的多用户权限控制设计及实现-总述【1】

    中大型的Web系统开发均需要权限的配置,基于多角色,多用户的操作权限管理是一个系统开发的基础.搭建好一套权限,用户,角色,页面一体的开发架构,可以用于后期业务的开发,同时也可用于不同业务的系统开发. ...

  7. 新浪微博sdk bug

    最近在做一个 iOS 的 cocos2d-x 项目接入新浪微博 SDK 的时候被“坑”了,最后终于顺利的解决了.发现网上也有不少人遇到一样的问题,但是能找到的数量有限的解决办法写得都不详细,很难让人理 ...

  8. 使用 Android Studio 跑新浪微博SDK Demo遇到的问题及解决

    概述 这是新浪微博官方 Android SDK Demo 使用 Android Studio 导入.编译并运行通过的版本. 源码:WeiboSdkDemo 官方项目请点击: weibo_android ...

  9. 很详细、很移动的Linux makefile教程:介绍,总述,书写规则,书写命令,使用变量,使用条件推断,使用函数,Make 的运行,隐含规则 使用make更新函数库文件 后序

    很详细.很移动的Linux makefile 教程 内容如下: Makefile 介绍 Makefile 总述 书写规则 书写命令 使用变量 使用条件推断 使用函数 make 的运行 隐含规则 使用m ...

随机推荐

  1. U盘占空间,但看不到内容——解决方案

    原因: u盘感染了病毒 .病毒把U盘里的文件都加上了隐藏属性和系统属性,所以看不到了,但仍占用存储空间. 解决办法: 1.在“运行“里面输入:cmd,回车:2.在cmd中进入U盘.比如你的U盘是H盘, ...

  2. c++使用stdint.h和inttypes.h

    我们有时候需要使用int有关的宏,比如PRId64,int64_t等,就需要包含那两个头文件. 由于那两个头文件是为c99默认使用的,c++要使用它可能要定义__STDC_FORMAT_MACROS, ...

  3. cordova for ios(android一样)添加插件

    1.进入当前工程文件夹 终端:cd ~/Desktop/ cd piao 2.添加插件 :cordova plugin add Basic device information (Device API ...

  4. CPU与内存的关系

    至今才对cpu和内存有一定了解了,下面有几个比喻的理解,很形象呦~ 1# 例如你要吃东西时,硬盘是锅,内存是碗,CPU是你,浅显点就是这样子了~ 2# 例如电脑是企业,内存是车间,cpu是生产线,硬盘 ...

  5. 在Python中调用glutInit遇到OpenGL.error.NullFunctionError的解决方法

    在window10 64bit + Python环境下,通过pip install PyOpenGL成功之后,无奈执行到glutInit()时候总是报错. OpenGL.error.NullFunct ...

  6. 。net初学

    这一周主要是对.net语法基础知识的简介以及一些作业练习,大部分还是与c语言有关联.一开始语法上有差异,写起代码来有困难,逻辑有点转换不过来.但是通过上周的练习,现在写起 作业来,还是挺快的.     ...

  7. Apache Thrift 跨语言服务开发框架

    Apache Thrift 是一种支持多种编程语言的远程服务调用框架,由 Facebook 于 2007 年开发,并于 2008 年进入 Apache 开源项目管理.Apache Thrift 通过 ...

  8. Unity3d + NGUI 的多分辨率适配

    一.当下移动设备的主流分辨率(数据来自“腾讯分析移动设备屏幕分辨率分析报告”) 1.1 iOS设备的分辨率主要有:   宽 高 宽高比 960 640 1.5 1136 640 1.775 1024 ...

  9. java指定路径写、读文件

    package com.util; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; ...

  10. IOS Socket 03-建立连接与登录

    1. 搭建python服务器 这里我们用到python服务器Socket Server.如何运行Server?下面介绍 1)通过百度云下载文件 http://pan.baidu.com/s/1i5yb ...