[转载]移动开发的三块重大技术点

mikel阅读(944)

[转载]移动开发的三块重大技术点 – 徐文兵 – 博客园.

1 2d 3d 图片和动画实现

2D图片:

2d 图像的绘制和显示

2d图像的二次处理 和显示

3d 动画

3d动画的播放

3d游戏引擎选择和sdk学习

3d游戏引擎的开发(如有机会在公司层面有资源提供)

基于3d动画引擎的事件捕捉,事件处理

2 基于终端的多媒体固件设备的API访问

语音:

调用API,打开麦克风,录制语音,并保存声音文件

对语音文件进行格式转换

播放指定的语音

对语音进行基于网络的传输

图像

调用API,打开终端的摄像头,设置摄像头参数,并获取图片,并保存图片格式。

对图片文件进行格式转化

浏览图片

对图片进行基于网络的传输

视频:

调用api,打开摄像头,设置摄像头得参数,获取视频文件,并按照一定格式保存

对视频文件进行格式转化

浏览并播放视频

对视频文件进行基于网络的传输

3 实现Android系统自带基础功能(如类似网络电话)

前一段了解了一个应用 网络电话

这个应用实现的功能是,用户拨打其他用户电话时,在对方电话也是智能手机且也有网络电话应用时,直接转化成网络电话通话,这样直接避开电信运行商,同时在用户体验上和直接打电话又没有太多的区别

对于这样一些应用要求,对于开发者要关注于以下几个方面

l  对原Android系统基础功能实现的了解

l  了解Android 提示方式

l  线程编程

l  网络服务接口互动

[转载]android实战系统二Android网络互动传输方案选择和实现

mikel阅读(826)

转载android实战系统二Android网络互动传输方案选择和实现 – 徐文兵 – 博客园.

前言:

在前面介绍过基于对话框形式,实现登陆和注册账号,没有细说Android终端和互联网的服务之间的交互实现,

在下面我将说明在在选择Android传输的方案选型分析过程,登陆和注册接口定义和实现.

1  Android传输方案分析和选型

在谈到android 传输方案,一般大家想到的就是 Socket传输,基于http的web服务,还有直接是http的post,get获取方式。

接下来是从 【网络实现条件】,【使用范围】, 【传输优缺点】, 【传输数据格式】,【综合开发成本】,【应用复杂度】等角度分析这三种传输方式,做为选择的判断条件。

下面是我对这三种传输方式的分析。

Socket传输 Web服务 Post,get获取数据方式
网络实现条件 端口:特定端口

协议:TCP,UDP

(目前无线网络非80端口的支持度低。目前移动梦网只支持wap和http协议)

端口 80

协议是http

端口 80

协议是http

使用范围 数据量大,格式简单,且实时性要求高的传输 标准的Web服务接口,接口固定 http形式,接口固定
传输优缺点 速度快,数据量小 速度一般,数据量大 速度一般,数据量一般
传输格式 自定义数据格式 SOAP Json
开发要求 客户端和服务器端:数据拼接,解析,数据完整验证

开发人员技术要求高

客户端:使用第三方web服务组件,拼接参数,解析回馈数据,过程复杂。

服务器端:创建web服务

开发人员技术要求:一般

客户端:直接调用android自带的json包解析json数据。过程简单

服务器端:生成基于httpHandler的ashx文件接口

开发人员的技术要求:低

综合成本 一般

综合以上分析:选择json+httphandler 的传输方式

2 登陆和注册两个接口实现

2.1 登陆接口实现

接口定义

http://192.168.1.1/gointel/UserHandler.ashx?Action=login&Account=xuwenbing&PassWord=xuwenbing

反馈的json格式:

{ActionResult:false,Reason:””账号和密码不一致””}

源码:


/*用户登陆
* 服务器检测用户的账号和密码是否一致,并返回结果
* */
public static Boolean Login(String Account ,String PassWord)
{
//Step One  从服务器接口中获取当前账号和密码的配对情况
Boolean actionResult=false;
String httpUrl="http://221.181.127.43/gointel/UserHandler.ashx?Action=login&Account="+Account+"&PassWord="+PassWord;
//httpGet 连接对象
HttpGet httpRequest =new HttpGet(httpUrl);

try
{
//取得HttpClinet对象
HttpClient httpclient=new DefaultHttpClient();

// 请求HttpClient,取得HttpResponse
HttpResponse  httpResponse=httpclient.execute(httpRequest);

//请求成功
if(httpResponse.getStatusLine().getStatusCode()==HttpStatus.SC_OK)
{
//取得返回的字符串
String strResult=EntityUtils.toString(httpResponse.getEntity());

JSONObject jsonObject = new JSONObject(strResult) ;
//获取返回值,并判断是否正确
actionResult=jsonObject.getBoolean("ActionResult");
}
}
catch(Exception e)
{
return false;

}
return actionResult;
}

2.2 注册接口实现

接口定义:

http://192.168.1.1/gointel/UserHandler.ashx?Action=register&Account=xuwenbing&PassWord=xxuwenbin&NiceName=ninilan;

反馈json格式:

{ActionResult:false,Reason:””用户名已存在””}

源码:


public static Boolean Register(String account, String passWord,
String niceName) {

Boolean actionResult=false;
String httpUrl="http://221.181.127.43/gointel/UserHandler.ashx?Action=register&Account="+account+"&PassWord="+passWord+"&NiceName="+niceName;
//httpGet 连接对象
HttpGet httpRequest =new HttpGet(httpUrl);

try
{
//取得HttpClinet对象
HttpClient httpclient=new DefaultHttpClient();

// 请求HttpClient,取得HttpResponse
HttpResponse httpResponse=httpclient.execute(httpRequest);

//请求成功
if(httpResponse.getStatusLine().getStatusCode()==HttpStatus.SC_OK)
{
//取得返回的字符串
String strResult=EntityUtils.toString(httpResponse.getEntity());

JSONObject jsonObject = new JSONObject(strResult) ;

//获取返回值,并判断是否正确
actionResult=jsonObject.getBoolean("ActionResult");
}
}
catch(Exception e)
{
return false;

}
return actionResult;
}

[转载]利用Callback=xml 绕过OAuth 验证,免去xAuth的申请

mikel阅读(1579)

[转载][05.24] 利用Callback=xml 绕过OAuth 验证,免去xAuth的申请 – akita – 博客园.

以前一直用 BasicAuth 作验证,不过 Sina 声明从六月份开始正式停止对 BasicAuth 的支持,因为保密性能太差,的确,谁也不希望自己的用户名和密码一直在天上飘来飘去,被拦截已经不是时间问题,而是必然的了。

然 而 Oauth 对我们这种小型爱好者有一个问题就是需要到网页端去点击那个确认授权,可是我们是可以让用户在软件内输入用户名密码的。虽然这样还是会让我(也就是第三方 软件提供方)直接接触到用户的用户名密码,但是我个人认为作为客户端可能这样是无可避免的,这也牵涉到一个用户信任度的问题,不然难道 Sina 要直接提供在线 TextBox 输入功能么。。。

不要偏题,本文重点不在OAuth的验证过程,那么接下来简单说明下 OAuth 的流程:

1.第三方服务商用自己的 key 和 secret 向服务器发起申请,获得 request token 这个 token 和用户没有任何关联,仅仅是服务商和 sina 沟通的结果;

2.第三方服务商根据 request token 将用户导向 Sina 提供的授权页面,一个 Web 页面(这个是重点,我们就是要绕过这一步,没人愿意在自己的软件里面转为Oauth验证添加一个 WebBrowser Control);

3.用户在 Web 页面中输入了用户名密码并授权后,页面执行 Callback,callback 的地址已经包含了一个 oauth verifier,可以看做用户授权证书;

4.第三方服务商带着第一步获取的 request token 和 第三步获取的 verifier 再次向 Sina 服务器发起申请,现在你将获得一个 access token,也就是通行证了。

好 阐述完毕 oAuth 的验证过程后,我们发现移动客户端中如果不借助 Basic Auth 和 xAuth(复杂的申请流程),很难在无页面导向的情况下获得 access token,网上查阅了很多资料,Android 就是使用了一个 webview 控件,其他平台要么就是申请 xAuth 要么就是网页形式,他们忽略了 Sina 很人性化的一个 callback = json/xml 的功能。

详细说明下这个问题,第三步中,如果 我们直接以sina 的页面导向形式 request 需要的 uri (类似 http://api.t.sina.com.cn/oauth/authorize?oauth_token={0}& oauth_callback={1})那么你获得的 response 肯定是一个 html 的页面。但是,Sina 的 authority API 提供了一个很tricky的功能(http://open.t.sina.com.cn/wiki/index.php/Oauth/authorize?retcode=0

简单说,如果你用(http://api.t.sina.com.cn/oauth/authorize?oauth_token={0}&oauth_callback=xml&userId= {1}&passwd={2})这个 Path 去 request Sina 的服务器,神奇的事情就发生了,返回会直接是一个 xml 格式的 callback,其中包含 token 和 verifier,接下来的事情就跟上面第四步一样咯,嘿嘿,绕过页面的感觉是不是很爽,不知道 Sina 会不会取消这样的 callback 因为毕竟这样还是会有一次需要用 userid 和 passwd 作为参数传递出去的,等于还是裸奔了一段。大家自己判断吧。(json 方式大概相同,就不说了。)

[转载]C# 新浪微博群发器

mikel阅读(947)

[转载]C# 新浪微博群发器 – FMN – 博客园.

通过新浪微博api群发微博,使用了sina提供的sdk,并对其进行小改,跳过了oauth页面认证。这个sdk用起来挺方便的。下面介绍实现方法,给有这方面需要的一个参考。

由于跳过了oauth页面认证,需要发送一次用户密码,不够安全,大家应该酌情使用。

我小改的sdk下载地址,将下载下来的文件夹复制到项目中:http://files.cnblogs.com/fmnisme/sinaApi.rar

sina官方文档中心:http://open.weibo.com/wiki/index.php/%E9%A6%96%E9%A1%B5

绕过oauth页面认证的方法使用了博客园akita 的方法,在此感谢,关于该方法的博客:http://www.cnblogs.com/btxakita/archive/2011/05/24/2055767.html

skd使用方法:

首先修改SDK中GlobalInfo类的appkey,appSecret为你在sina申请到的appKey,appSecret;

var httpRequest = HttpRequestFactory.CreateHttpRequest(Method.GET) as HttpGet;
//因为群发博客,所以用数组存放了用户信息,x代表数组下标。
                httpRequest.GetRequestToken();
                string url = httpRequest.GetAuthorizationUrl();
                GlobalInfo.requestTokens[x] = httpRequest.Token;
                GlobalInfo.requesTokenSecrets[x] = httpRequest.TokenSecret;
                httpRequest.GetVerifier(“用户名[x]”,“密码[x]”x); 
                httpRequest.GetAccessToken();
                GlobalInfo.requestTokens[x] = httpRequest.Token;
                GlobalInfo.requesTokenSecrets[x] = httpRequest.TokenSecret;
                var sendUrl = "http://api.t.sina.com.cn/statuses/update.xml?";
                httpRequest2.Request(sendUrl, "status=" + HttpUtility.UrlEncode(“微博内容”));

下面贴出代码:


using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using LeoShi.Soft.OpenSinaAPI;
using System.Web;
using System.Threading;

读取配置文件并获取相应的accessToken,配置文件的格式为:username&password,每个这样的占一行,使用记事本写就行了。

private void btn_readIni_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
txt_filePath.Text = openFileDialog1.FileName;
}

if (txt_filePath.Text != “”)
{
FileStream fs = new FileStream(txt_filePath.Text, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(fs);
while(!sr.EndOfStream)
{
string str = sr.ReadLine();;
if (str == “”)
continue;
string[] strArr = str.Split(‘&’);
userInfoList.Add(new string[2] {strArr[0].Trim(),strArr[1].Trim()});
}
sr.Close();
fs.Close();
}
btn_send.Enabled = false;
initSina();
btn_send.Enabled = true;
lb_status.Text = “成功连接微博,初始化完成”;
}

private void initSina()
{
for (int x = 0; x < userInfoList.Count; x++) { if (userInfoList[x][0]=="") break; var httpRequest = HttpRequestFactory.CreateHttpRequest(Method.GET) as HttpGet; httpRequest.GetRequestToken(); string url = httpRequest.GetAuthorizationUrl(); GlobalInfo.requestTokens[x] = httpRequest.Token; GlobalInfo.requesTokenSecrets[x] = httpRequest.TokenSecret; httpRequest.GetVerifier(userInfoList[x][ 0], userInfoList[x][ 1],x); httpRequest.GetAccessToken(); GlobalInfo.requestTokens[x] = httpRequest.Token; GlobalInfo.requesTokenSecrets[x] = httpRequest.TokenSecret; } } [/csharp] 发送微博: [csharp] private void btn_send_Click(object sender, EventArgs e) { Thread sendWeiboThread = new Thread(new ThreadStart(sendWeibo)); sendWeiboThread.Start(); } private void sendWeibo() { Thread.CurrentThread.IsBackground = true; Control.CheckForIllegalCrossThreadCalls = false; btn_send.Enabled = false; btn_readIni.Enabled = false; for (int m = 0; m < int.Parse(txt_sendTimes.Text); m++) { times++; for (int x = 0; x < userInfoList.Count; x++) { lb_status.Text = userInfoList[x][0] + " " + x.ToString() + "/" + userInfoList.Count; var httpRequest2 = HttpRequestFactory.CreateHttpRequest(Method.POST); httpRequest2.Token = GlobalInfo.requestTokens[x]; httpRequest2.TokenSecret = GlobalInfo.requesTokenSecrets[x]; var sendUrl = "http://api.t.sina.com.cn/statuses/update.xml?"; httpRequest2.Request(sendUrl, "status=" + HttpUtility.UrlEncode(txt_weibo.Text + " " + times.ToString())); Thread.Sleep(150); } } btn_send.Enabled = true; btn_readIni.Enabled = true; lb_status.Text = "发送完成。"; } [/csharp]

[转载]无名良品-首页优化(Flash篇)

mikel阅读(1040)

[转载]无名良品-首页优化(Flash篇) – zaohe – 博客园.

Flash的首屏应用

简介:

良品首页3.0版本在设计的时候,想在首屏添加一些特殊的效果,例如彩蛋之类,所以整个首屏使用了Flash,在高品质的显示效果和动画效果背后带来了大量问题,下面来一一分析。

问题:

1. 版面大,使用了1000 * 500的分辨率,分2屏展示。

2. 高品质的图片,带宽需求高。

3. 灵活的展示方式,图片个数不确定,请求数目多,平均每次版本 10张图片左右。

4. 图片分布在CDN的不同服务器上,加上FlashSWF文件,图片的配置文件,Flash的资源一共分布在6个服务器上,cross-domain 验证开销巨大。

5. 丢失Ref问题

6. 某些用户设备无对Flash的支持

各个版本以及改进:

1. 上线前的版本:

图片全部都是PNG24,所有图片(2屏)同时加载,首次打开页面,所有的请求数在34个左右,图片大小在1.1M左右。一般网速下打开首屏需要 10-15秒。

2. 正式上线的版本:

与上线前得版本相比,打开页面时,仅加载第一屏的图片,第二屏图片延迟加载,首屏的请求数下降3-4个,图片大小降低到800K 左右。打开首屏8-10秒。

此时问题依然严重,所有人都反映首屏加载慢,我们说服了设计师,将首屏的PNG替换成JPG,画质在85以上,由设计师自由调整,保证画质的前提下,大大降低图片大小。

3. 优化图片后的版本:

加载图片的大小降低一半,请求数未变,首屏图片降低到400K左右,打开时间在3-5秒。

到了这个时候,图片的大小已经没法优化,Flash在未加载完时,首屏一片空白,至少3秒才能看到图片加载,网速慢的情况下5-10秒钟都是空白,仔细分析发现时间有很大部分消耗在Flashcross-domain验证上,在验证时,会阻塞对此服务器的请求。

此时需要解决的问题是,将空白去掉,加载完成Flash前显示首屏的图片,等Flash加载完成后,替换掉Dom元素。

4. 加载Flash前显示首屏图片

部分图片重复加载,整个首屏的请求数上升8个左右,图片的请求大小上升到900K,但是由于图片在Flash加载前已经加载,Flash加载的图片仅是在缓存中加载,所以,图片整体大小并未增加,但是缓存机制无法保证在所有用户都起作用。

设计师对此次改进依然不满意,因为有2-3秒内首屏没有动画效果,所以一个Flash首屏的JS提单版本开发出来,在整个Flash加载以及内部的图片加载完成后隐藏JS版生成的Dom元素,隐藏而不是移除的原因是为了后台的统计。

5. JS部分替代版本:

所有首屏图片重复加载,对首屏的影响跟前面的版本相比差别不大,增强了用户体验,在不支持Flash的浏览器中依然可以使用。

Flash 版本从上线到这个时候已经半个月,无论前端还是BI部门对这个首屏都不满,特别是BI部门,后台系统对点击数据的统计是基于 a 标签的,Flash版本使BI部门只能手工统计,而且不准确。我们找了设计师后,确认一些额外的超炫功能暂时不再上线,我们就使用完全的JS版本替代Flash版本。

6. JS完全替代版本:

首页,首屏的请求数降低到25个,首屏资源的大小下降到370K左右,加载时间2-3秒,中间没有白屏等待Flash加载的现象。

对比表格

版本 请求数 首屏资源大小 首屏完全加载完成后资源大小 加载时间 加载完成前显示空白时间
上线前 34 1.1M 1.1M 10-15s 2-5s
上线后 30 900K 1.1M 8-10s 2-5s
优化图片后 30 400k 700K 5-8s 2-5s
预加载Flash图片 40 700K 1.1M 5-8s 无(2秒内可以显示图片)
JS部分替代 44 700K 1.5M 5-8s 无(2秒内可以显示图片)
JS完全替代 25 370k 520K 2-3S

*首屏的加载时分屏加载的,第一屏切到第二屏前加载第二屏的图片。

*加载时间是按首次加载算的,浏览器未缓存的情形

总结:

Flash在高访问量的首页应用有一定的限制,特别是作为展示多个图片的容器,因为这种网站一般使用CDN服务器,所以图片会分布在多个服务器上,由此带来的cross-domain问题很难解决,其他的诸如丢失Ref等问题网络上都有现成的解决方案(模拟Form提交)。

[转载]SQL常用技术总结

mikel阅读(894)

[转载]SQL常用技术总结 – Wendy ‘s – 博客园.

SQL语句是我们在书写数据存储,查询时必具备的知识,以下把自己个人觉得可用的知识点总计如下:

1、对于存在自动编号的Insert插入语句,返回当前插入行的自动编号ID

eg: Insert into User(uId,Uname,Sex,Email) values(@uId,@UName,@sex,@email)

select @@identity

这样就会返回当前插入语句对应自动编号ID值

2、书写SQL存储过程事务, 在存储过程中你可以执行很多复杂的操作,带事务的存储过程,这样就会保持数据的一致性,要么都执行

成功,在中途出现的失败,系统会自动回滚。

Create proc MyProcTest

(

@UId varchar(10),

@UName varchar(30),

@sex bit,

@email varchar(30)

)

as

begin transaction

Insert into User(uId,Uname,Sex,Email) values(@uId,@UName,@sex,@email)

if @@error=0

commit

else

rollback

end

3、复杂存储过程书写,此翻页存储过程是SQL2000的,当然后继2005版本,出现一些新特性,翻页过程可以简化,如下就把2000下书写的

程序才出来共享

ALTER PROCEDURE GetDataByPage

(

@PageNum INT, –页码

@PageSize INT –每页的数据数

)

AS

DECLARE @totalNum int

SET @totalNum=(SELECT COUNT(*) FROM lqk15)

DECLARE @totalPage INT–获得总页数

SET @totalPage=CEILING(@totalNum*1.0/@PageSize)

IF (@PageNum>@totalPage)

set @PageNum=@totalPage–设置起始号,和结束号

DECLARE @startIndex INT

DECLARE @endIndex   INT
SET @startIndex =(@PageNum-1)*@PageSize

SET @endIndex = @PageNum*@PageSize-1

CREATE TABLE #Temp (classid VARCHAR(14),IdIndex  INT IDENTITY (1,1))
INSERT INTO #Temp SELECT ksh FROM lqk15 ORDER BY ksh
SELECT ksh,xm FROM lqk15 inner join #Temp ON lqk15.ksh=#Temp.classid WHERE IdIndex>=@startIndex and IdIndex<=@endIndex ORDER BY ksh
DROP TABLE #Temp

通过此代码可以对负责SQL语句,临时表的书写有一定的了解和掌握。看明白了,理解的就深入了。

4、查询SQL语句格式化

前天遇到这样个需求要在查询的数据集各字段上带上单引号和逗号,然后导出成txt文本,刚开始打算把查询出来的结果存入记事本后,然后通过正则表达式匹配后进行处理,后来发现SQL就能实现此过程

期望的数据是这样的:

‘03023106’,’80’,’合格’

‘03023113’,’50’,’不合格’

‘03023126’,’88’,’合格’

通过SQL查询语句查询满足此格式:

select ”’+stId+”’,’,”+Score+”’+’,”+Result+”’ from StudentScore

查询出来,直接复制到txt文件中即可。

5、存储过程加密,处于性能和业务逻辑操作考虑,我们往往把一些复杂的SQL语句书写为存储过程,若不想让别人看到存储过程业务逻辑,可以对存储过程进行

加密处理,此过程是不可逆的,所以加密前要对SQL脚本进行一定的备份,每次操作时保证二则同步

加密存储过程语句如下:

Create proc MyProcTest with encryption

as

select * from Exam_Users

执行完成后,查看存储过程,则此存储过程上加了一把小锁,修改按钮也不可操作

如果要修改此存储过程,只能重新执行 Alert proc MyProcTest去覆盖之前的存储 过程。

6、SQL语句实现数据库本地的备份

Backup database Users to Disk ‘D:\\News20110526.bak’

此SQL语句即可实现简单的把users数据库备份在本地的D盘根目录下,名字为News20110526.bak

此过程有两个注意点:

1)、操作可能在客户机,而备份的文件,在SQL数据库服务器上

2)、如果想实现备份文件在当前机器上,则需要额外的操作,与之相关的数据库还原也一样,如果文件在本地想还原数据库到服务器上,单单执行

类似如上语句,则会失败报错。

先写这么多了,一如既往的关注博客园的相关内容。

[转载]分享50个优秀的电子商务网站设计案例

mikel阅读(1566)

[转载]分享50个优秀的电子商务网站设计案例 – 梦想天空(山边小溪) – 博客园.

对于电子商务网站来说,也许销售更多产品比漂亮的外观设计更重要,不过漂亮的东西总是能给用户留下深刻的印象。今天,这篇文章与大家分享50个优秀的电子商务网站设计作品案例,希望能带给你灵感。

Free People

Free People

Me & Mommy-to-be

Me & Mommy-to-be

Keedo

Keedo

UNIQLO

UNIQLO

CellyShop

CellyShop

Big Brown Box

Big Brown Box

Junstil

Jungstil

Little Catwalk

Little Catwalk

Shoon

Shoon

Bagolitas

Bagolitas

Bridge 55

Bridge55

Shop Curious

Shop Curious

Itself

Itself

La Llevo Puesta

La Llevo Puesta

Morphica

Morphica

Wunderbloc

Wunderbloc

Rock Pillars

Rock Pillars

Tilly Moss

Tilly Moss

Cacties

Cacties

Bad Designer Threads

Bad Designer Threads

La Licious

La Licious

Mozilla Store

Mozilla Store

Habitat Shoes

Habitat Shoes

A Better Tomorrow

A Better Tomorrow

Jungle Crazy

Jungle Crazy

Subnormals

Subnormals

Bored

Bored

Brand Neusense

Brand Neusense

Bras & Honey

Bras and Honey

Lanx

Lanx

Martique

Martique

Oraya

Oraya

Costume Studio

Costume Studio

Galante

Galante

Von Dutch

Von Dutch

Bonjour Mon Coussin

Bonjour Mon Coussin

Red is White

Red is White

Composition

Composition

Wire & Twine

Wire & Twine

Mouse to Minx

Mouse to Minx

Like.com

Like.com

Fugitive Toys

Fugitive Toys

One Horse Shy

One Horse Shy

Cosmicsoda

Cosmicsoda

Threadless

Threadless

evelMerch

Evel

ShoeGuru

ShoeGuru

Urban Originals

Urban Originals

Emptees

Emptees

Mia & Maggie

Mia & Maggie

[转载]iphone开发我的新浪微博客户端-用户登录准备篇(1.1)

mikel阅读(916)

[转载]iphone开发我的新浪微博客户端-用户登录准备篇(1.1) – 水的右边 – 博客园.

首先说一下我这个的实现思路,登录支持多个账号,也就是说可以保存多个微博账号登录的时候选择其中一个登录。多个账号信息保存在SQLite的数据库中, 每一个账号信息就是一条记录, 当用户启动微博客户端的时候去取保存在SQLite数据库中的账号记录信息,然后把这些在界面中以列表的形式展示出来,用户可以点击其中的一个账号进入微 博,如果如果启动微博客户端的时候检查到SQLite数据库中一个账号都没有的时候,程序自动显示用户授权认证页面,本客户端是基于oauth认证实现 的,所以使用新微博账号前需要进行授权认证,一个账号只需要第一次做一下授权认证然后会把user_id、Access Token和Access Secret以及用户名称和头像小图标一起保存到sqlite的数据库中,下次登录的时候可以不在需要输入账号和密码,直接在界面中点击已经选择的用户小 图标进入微博。关于Access Token和Access Secret这个我就不解释了,不懂的可以google一下oauth的知识,其实我前面做Android版本的微博客户端的时候写过一篇比较详细的介绍 过关于oauth的随笔可以翻出来参考一下。

从上面的思路来看用户登录有两种可能性,第一种数据库中尚无任何账号信息;第二种数据库中已经包含了一个或以上的账号信息。这两种情况显示不同的用户界面,两种情况的实现过程看下面的描述。

第一种情况实现过程:1.查询sqlite数据库 –>2.无账号记录显示需要进行授权认证的提示信息界面(上图6) –>3.在上一步界面中点击开始按钮进入用户授权界面–>4.用户输入自己的微博账号和密码点击确定然后关闭用户授权界面(上图7)–>5.程序根据用户的授权获取这个账号的信息保存到sqlite库–>6.显示用户登录选择界面默认账号就是用户刚刚授权的账号

第二种情况实现过程:1.查询sqlite数据库 –>2.已有账号记录获取所有账号信息并且显示用户登录选择界面 –>3.读取上一次登录的账号作为界面的默认选择用户显示(如上图2)–>4.点击添加按钮显示账号添加界面(如上图4)–>5.点击切换按钮显示账号选择列表界面(如下图3)–>6.点击删除按钮显示账号删除确认界面(如下图5)

从上面的实现过程看涉及到的主要知识点:1.sqlite数据库操作(创建数据库、创建数据表、插入数据记录、读取数据记录);2、oauth授权认证 (已经由sdk实现了调用相关方法即可);3、NSUserDefaults存取(用来记录上一次的登录账号)

现在正式开始动手做一一些准备工作:

一、到 http://code.google.com/p/minblog4sina/(关于这个可以参看:iphone开发我的新浪微博客户端-开篇)把我的这个sdk项目源代码checkout到本地,然后用xcode打开这个工程,在这个sdk工程的基础上开发微博客户端应用。

二、设计sqlite数据库用来保存用户账号信息,我是用firefox的一个名为SQLite Manager的插件来创建sqlite数据库,这个使用非常简单就不介绍了自己装起来就知道怎么使用,我用它创建了一个名为weibo.sqlite的 数据库,然后这这个数据库中创建了一个名为loginUser的表用来保存账号信息,loginUser表的,这个表包含5个字段分别用来保存 user_id、账号昵称、Access Token、Access Secret、账号小图标,具体如下图:


三、用xcode打开工程,然后把上一步完成的weibo.sqlite文件添加到工程的Resources文件夹中.

四、在工程中添加类型为Objective-C class名为Sqlite的类用来负责sqlite库的创建、记录的读取、记录的添加等操作,同时在Xcode的左边树Frameworks右键添加名 为libsqlite3.dylib的Framework,这个类库封装了sqlite库的操作。

五、打开Sqlite.h添加如下代码,在这个文件中首先import了sqlite3.h,这个就是Frameworks中的 libsqlite3.dylib类库,然后又import了User.h,这个是MinBlog4Sina的sdk中的用户账号对象类。又声明的4个方 法分别是数据库初始化、获取用户记录列表、添加用户记录、删除用户记录。

#import <Foundation/Foundation.h>
#import <sqlite3.h>
#import "User.h"

@interface Sqlite : NSObject {
sqlite3 *database;
}

-(Sqlite *)init;
-(NSMutableArray *)getUserList;
-(BOOL)addUser:(User *)user;
-(BOOL)delUser:(NSString *)name;

@end

六、打开Sqlite.m添加如下代码,这个文件里实现了上述的几个方法,所谓的数据库创建其实就是把上面第三个步骤中添加到工程的weibo.sqlite文件拷贝到本软件的目录中,在下面的-(Sqlite *)init方法中进行了拷贝操作。还有就是用户记录读取和添加中关于BLOB类型的数据处理,这里存储的是图片数据了,在添加的时候把图片转换成bytes类型,读取的时候把bytes类型转换成图片。

#import "Sqlite.h"

@interface Sqlite(private)

-(void)createDatabaseIfNeeded:(NSString *)filename;
-(NSString *)dataFilePath;
-(BOOL)openDatabase;
-(void)closeDatabase;

@end

@implementation Sqlite
//初始化数据库
-(Sqlite *)init
{
if (self= [super init]) {
[self createDatabaseIfNeeded:@"weibo.sqlite"];
}
return self;
}

-(BOOL)delUser:(NSString *)name
{
BOOL sucess=[self openDatabase];
if (sucess) {
NSString *sql=[[NSString alloc] initWithFormat:@"delete from loginUser where screen_name='%@'",name];
char *errorMsg;
if (sqlite3_exec(database, , NULL, NULL, &errorMsg)!=SQLITE_OK) {
NSLog(@"del User error:%s",errorMsg);
sqlite3_free(errorMsg);
}else {

}
;
}
[self closeDatabase];
return sucess;

}

//添加用户记录
-(BOOL)addUser:(User *)user
{
BOOL sucess=[self openDatabase];
if (sucess) {
NSString *sql=[[NSString alloc] initWithFormat:@"insert into loginUser(user_id,screen_name,key,secret,icon)values(?,?,?,?,?)"];
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, , -1, &statement, nil)==SQLITE_OK)
{

NSString *uid=[NSString stringWithFormat:@"%@",user.userId];
const char *user_idChar=[uid UTF8String];
sqlite3_bind_text(statement, 1, user_idChar, strlen(user_idChar), NULL);

const char *screen_nameChar=[user.screen_name UTF8String];
sqlite3_bind_text(statement, 2, screen_nameChar, strlen(screen_nameChar), NULL);

const char *keyChar=[user.key UTF8String];
sqlite3_bind_text(statement, 3, keyChar, strlen(keyChar), NULL);

const char *secretChar=[user.secret UTF8String];
sqlite3_bind_text(statement, 4, secretChar, strlen(secretChar), NULL);

NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:user.profile_image_url]];
sqlite3_bind_blob(statement, 5, [imageData bytes], [imageData length], NULL);

if(sqlite3_step(statement)==SQLITE_DONE)
{
NSLog(@"done");
}else {
NSLog(@"error");
}
sqlite3_finalize(statement);
}
;
}
[self closeDatabase];
return sucess;
}

//获取用户记录列表
-(NSMutableArray *)getUserList
{
NSMutableArray *list;
BOOL sucess=[self openDatabase];
if (sucess) {
NSString *query=@"select * from loginUser";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)==SQLITE_OK)
{
list=[[[NSMutableArray alloc] init]autorelease];
while (sqlite3_step(statement)==SQLITE_ROW) {
char *user_idChar=(char *)sqlite3_column_text(statement, 0);
char *screen_nameChar=(char *)sqlite3_column_text(statement, 1);
char *keyChar=(char *)sqlite3_column_text(statement, 2);
char *secretChar=(char *)sqlite3_column_text(statement, 3);
int bytes = sqlite3_column_bytes(statement, 4);
const void *value = sqlite3_column_blob(statement, 4);

NSString *user_id=[[NSString alloc] initWithUTF8String:user_idChar];
NSString *screen_name=[[NSString alloc] initWithUTF8String:screen_nameChar];
NSString *key=[[NSString alloc] initWithUTF8String:keyChar];
NSString *secret=[[NSString alloc] initWithUTF8String:secretChar];
UIImage *icon;
if( value != NULL && bytes != 0 ){
NSData *data = [NSData dataWithBytes:value length:bytes];
icon=[UIImage imageWithData:data];
}
User *user=[[User alloc] init];
user.userId=user_id;
user.name=screen_name;
user.key=key;
user.secret=secret;
if (icon) {
user.icon=icon;
}

[list addObject:user];
[user release];
[user_id release];
[screen_name release];
[key release];
[secret release];
}
sqlite3_finalize(statement);
}
}
[self closeDatabase];

return list;
}


//根据是否已经存在数据库决定是否要创建数据库
-(void)createDatabaseIfNeeded:(NSString *)filename
{

NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
NSFileManager *filemanage=[NSFileManager defaultManager];
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory=[paths objectAtIndex:0];
NSString *writableDBPath=[documentsDirectory stringByAppendingPathComponent:filename];
BOOL sucess=[filemanage fileExistsAtPath:writableDBPath];
if (sucess) {
return;
}
NSString *defaultDBPath=[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:filename];
NSError *error;
sucess=[filemanage copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!sucess) {
NSLog(@"create Database rror:%@",[error localizedDescription]);
}
[pool release];
}

//获取数据库文件路径
-(NSString *)dataFilePath
{
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath=[[paths objectAtIndex:0] stringByAppendingPathComponent:@"weibo.sqlite"];
return documentPath;
}

//打开数据库
-(BOOL)openDatabase
{
int returnValue=sqlite3_open([[self dataFilePath] UTF8String], &database);
if (returnValue!=SQLITE_OK) {
sqlite3_close(database);
NSLog(@"open Database error:%@",sqlite3_errmsg(database));
return NO;
}else {
return YES;
}

}

//关闭数据库
-(void)closeDatabase
{
if (sqlite3_close(database)!=SQLITE_OK) {
NSLog(@"close Database error:%@",sqlite3_errmsg(database));
}
}

@end

七、在工程中添加类型为Objective-C class名为Global的类用来提供一些非常常用的公用方法。

八、打开Global.h添加如下代码,声明了2个方法,一个是获取png的图片资源、另外一个是进行时间格式化输出的方法,这2个方法都是被声明成+类型,这样方便使用,这2个方法在后面篇章中经常会用到。

#import <Foundation/Foundation.h>


@interface Global : NSObject

+(UIImage *) pngWithPath:(NSString *)path;
+(NSString *) dateInFormat: (time_t)dateTime format:(NSString*) stringFormat;
@end

九、打开Global.m添加如下代码

#import "Global.h"


@implementation Global

+(UIImage *) pngWithPath:(NSString *)path
{
NSString *fileLocation = [[NSBundle mainBundle] pathForResource:path ofType:@"png"];
NSData *imageData = [NSData dataWithContentsOfFile:fileLocation];
UIImage *img=[UIImage imageWithData:imageData];
return img;
}

+(NSString *) dateInFormat: (time_t)dateTime format:(NSString*) stringFormat
{
char buffer[80];
const char *format = [stringFormat UTF8String];
struct tm * timeinfo;
timeinfo = localtime(&dateTime);
strftime(buffer, 80, format, timeinfo);
return [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
}

@end

到处准备篇的工作就已经算是完成了,主要的编码工作就是sqlite的数据库操作了,在下一篇中我们会在这个基础上开始用户功能的开发。

[转载]如何在SQL Server中使用正则表达式

mikel阅读(934)

[转载]如何在SQL Server中使用正则表达式 – 陈希章 – 博客园.

最近处理客户一个需求,是要在SQL Server中对某个列进行校验,使用正则表达式保证该列必须是一个邮件地址。

我们都知道,SQL Server的T-SQL中默认是没有这样的语法的。找了一些资料,下面这个例子还不错,分享给大家参考

http://www.codeproject.com/KB/database/xp_pcre.aspx

大致步骤是

1.下载他提供的那个压缩包,里面有源代码和安装脚本

2.将DLL复制到SQL Server规定的目录

3.运行INSTALL.sql这个脚本

大致使用的效果如下

SELECT master.dbo.fn_pcre_match('billg@microsoft.com','^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$')

这句脚本的意思是,根据后面的正则表达式(一个email的规则)匹配前面的字符串.如果返回1的话,表示匹配到了,否则返回0.很显然,

image

很显然,我们可以根据这个做法设置某个字段的约束。例如下面这样

image

上述做法是通过扩展存储过程来实现的功能,需要通过C++来编写。

当然,如果SQL Server 是2005或者以后的版本,也可以通过托管代码来实现。关于这个话题的基本概念,你可以参考 http://msdn.microsoft.com/en-us/library/ms254498(v=vs.80).aspx

我写好了一个例子,给大家参考:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections;
using System.Text.RegularExpressions;

public partial class UserDefinedFunctions
{
    /// <summary>
    /// 这是一个进行正则表达式验证的函数.作者:陈希章
    /// </summary>
    /// <param name="ValidOption">选项,0为用户自定义,1为网址,2为邮件地址,3为邮政编码</param>
    /// <param name="ValidString">要验证的字符串</param>
    /// <param name="ValidPatten">用户自定义的正则表达式规则</param>
    /// <returns></returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static bool RegExValidate(int ValidOption, string ValidString, string ValidPatten)
    {
        string strRegExPatten = null;
        switch (ValidOption)
        {
            case 0: { strRegExPatten = ValidPatten; break; }
            case 1: { strRegExPatten = @"^[a-zA-Z0-9\-\.]+\.(com|org|net|mil|edu|COM|ORG|NET|MIL|EDU)$"; break; }
            case 2: { strRegExPatten = @"^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$"; break; }
            case 3: { strRegExPatten = @"^[0-9]{4}\s{0,1}[a-zA-Z]{2}$"; break; }
        }

        if (Regex.IsMatch(ValidString,strRegExPatten))
            return true;
        else
            return false;
    }
};

[转载]快速开发新浪微博的firefox插件(下)

mikel阅读(1036)

[转载]快速开发新浪微博的firefox插件(下) – Fei He – 博客园.

上篇主要讲了讲firefox插件的机制,接着我们来看快速开发一个firefox插件中我面临的第二个问题—-Oauth授权(开始开发的时候只是想着快速开发完成,当然授权这块最快的方案自然就是basic auth,但是新浪微博6月1号以后就不支持basic auth了。)。

Oauth的官网上说是这样描述它的用途:

An open protocol to allow secure API authorization in a simpleand standard method from desktop and web applications.

在Oauth的官网上对Oauth有详尽的描述以及不用语言对于Oauth的实现,对于我的开发我需要了解的其实非常简单.

首先对于Oauth有三个url是必须了解的:

  • Request Token URL
  • User Authorize URL
  • Access Token URL

另外,我们还需要了解针对这些URL相应的一些参数:

  • oauth_consumer_key
  • oauth_consumer_secret
  • oauth_signature_method
  • oauth_signature
  • oauth_timestamp
  • oauth_nonce
  • oauth_version

具体这些URL和参数有什么作用呢?我们结合新浪微博来说。对于开发一个新浪微博的应用,你必须在新浪微博申请一个app key和app key secret, 有了它们我们就可以开始了。新浪的Oauth是这样一个流程:

  1. 第 一次你需要做的是Request Token URL,对于新浪微博就是http://api.t.sina.com.cn/oauth/request_token。接着就是请求的参数了,这个时候 你需要把你申请的app key作为oauth_consumer_key, 而把你的app key secret作为oatu_consumer_secret。oauth_signature_method就是你的加密算法,oauth支持 HMAC-SHA1, RSA-SHA1, PLAINTEXT这三种算法,而新浪微博指定需要HMAC-SHA1,所以对与oauth_signature_method我们这里就是HMAC- SHA1了。oauth_timestamp就是请求的时间戳,这个时间戳的范围是现在1970 00:00:00 GMT的秒数,而且每次请求的时间戳必须大于上次的。oauth_nonce就是随机生成的一个字符串,就是为了防止重复请求。 oauth_version现在只能是1.0。最后我们需要对所有参数使用oauth_signature_method指定的加密算法加密作为 oauth_signature。对于请求的参数,我们有两种绑定方式:Get: 拼URL;Post: 放在Http heads里。 有了URL和参数我们就可以发起我们的请求。当新浪微博接受我们的请求检查合法后,会返回给我们一个未授权的oauth_token和 oauth_token_secret,因为我们还没有获得用户的授权。
  2. 有 了上一步的请求返回的oauth_token和oauth_token_secret,在进行下一步请求之前我们需要根据我们应用的类型决定我们下一步的 策略。如果我们是一个web应用,我们有自己的域名我们就可以选择使用callback_url的方式,但是我要做得是一个firefox插件这种方式显 然不合适,那么我只有选择使用verify PIN的方式了。确定了这个,我们请求User Authorize URL(对于新浪微博就是http://api.t.sina.com.cn/oauth/authorize)。这次的参数和上次的参数还是有一些小小 的差别的:首先,我们这次需要用上次请求返回的oauth_token作为oauth_consumer_key, 使用返回的 oauth_token_secret作为oauth_consumer_secret,oauth_signature_method不 变,oauth_timestamp根据这次请求的时间重新生成,oauth_nonce重新生成,oauth_version不变,而 oauth_signature根据这次的参数生成。在User Authorize过程中,我们要加一个参数callback_url。因为我们采用verify PIN的方式,所以这个参数我可以指定为xml。因为这一步就用户授权,那么我们自然还是要输入我们需要访问的新浪微博账号的用户名和密码,分别作为 userId, passwd。新浪微博接收到我们的User Authorize请求检查合法后,会以xml格式返回给我们一个oauth_verifier,证明我们的应用获得了用户的授权。
  3. 有 了oauth_verifier, 加上我们第一步获得oauth_token和oauth_token_secret,其他参数还是按照前面的规则(这次不需要 oauth_callback)构成我们的参数,我们来请求Access Token URL(对于新浪微博就是http://api.t.sina.com.cn/oauth/access_token)。 这一次新浪微博会返回access_token和access_token_secret, 我们授权就通过了。而这个access_token和access_token_secret当我们以后请求新浪微博API时,会作为 oauth_consumer_key和oauth_consumer_secret出现。比如我们要发送微博,新浪微博发送微博的API是 http://api.t.sina.com.cn/statuses/update.json。这时我们还是按照前面的规则组织参数(以 access_token作为oauth_consumer_key, access_token_secret作为oauth_consumer_secret),同时加上参数status,值为我们需要发送的内容就OK 了。

到这一步,我们还不算完,因为我们需要在 firefox的插件中调用新浪微博的oauth。我们都知道JavaScript直接调用新浪微博的API是跨域请求,这是不被允许的。而我们通常解决 跨域请求的方式主要就是:Jsonp, flash,iframe。这里面我们唯一可以尝试是jsonp,而jsonp是需要server端也就是新浪微 博配合的,而实际上新浪微博是不支持jsonp的。那是不是我们没有办法了呢?

@mozilla.org/xmlextras/xmlhttprequest;1

查看了MDN的文档,发现firefox自己的Xmlhttprequest可以,而且可以指定同步还是移步,然后在callback方法里解析返回参数。具体如何实现MDN。

ps:这个插件的代码是作为一个其他应用的一部分,这涉及其他人的劳动,有兴趣可以发站内信或邮件我会单独发一份(只包含firefox插件部分)给你。